Panduan Discovery.js: Mulai Cepat

Panduan ini dan panduan berikut akan memandu Anda melalui proses membuat solusi berdasarkan proyek Discovery.js . Tujuan kami adalah membuat inspektur untuk dependensi NPM, yaitu antarmuka untuk memeriksa struktur node_modules .



Catatan: Discovery.js berada pada tahap awal pengembangan, jadi seiring waktu, sesuatu akan menyederhanakan dan menjadi lebih bermanfaat. Jika Anda memiliki ide tentang cara meningkatkan sesuatu, tulis kepada kami .

Anotasi


Di bawah ini Anda akan menemukan gambaran umum konsep-konsep kunci dari Discovery.js. Anda dapat mempelajari seluruh kode manual dalam repositori di GitHub , atau Anda dapat mencoba cara kerjanya secara online .


Kondisi awal


Pertama-tama, kita perlu memilih proyek untuk dianalisis. Ini bisa berupa proyek yang baru dibuat atau yang sudah ada, yang utamanya adalah berisi node_modules (objek analisis kami).


Pertama, instal paket inti discoveryjs dan alat konsolnya:


 npm install @discoveryjs/discovery @discoveryjs/cli 

Selanjutnya, luncurkan server Discovery.js:


 > npx discovery No config is used Models are not defined (model free mode is enabled) Init common routes ... OK Server listen on http://localhost:8123 

Jika Anda membuka http://localhost:8123 di browser, Anda dapat melihat yang berikut:


Penemuan tanpa konfigurasi


Ini adalah mode tanpa model, yaitu mode ketika tidak ada yang dikonfigurasi. Tetapi sekarang, dengan menggunakan tombol "Muat data", Anda dapat memilih file JSON apa saja, atau cukup seret ke halaman dan mulai analisis.


Namun, kami membutuhkan sesuatu yang spesifik. Secara khusus, kita perlu melihat struktur node_modules . Untuk melakukan ini, tambahkan konfigurasi.


Tambahkan konfigurasi


Seperti yang mungkin Anda perhatikan, pesan No config is used ditampilkan ketika server mulai. Mari kita membuat file konfigurasi .discoveryrc.js dengan konten berikut:


 module.exports = { name: 'Node modules structure', data() { return { hello: 'world' }; } }; 

Catatan: jika Anda membuat file di direktori kerja saat ini (yaitu, di root proyek), maka tidak ada lagi yang diperlukan. Jika tidak, Anda perlu meneruskan path ke file konfigurasi menggunakan opsi --config , atau mengatur path di package.json :


 { ... "discovery": "path/to/discovery/config.js", ... } 

Mulai ulang server sehingga konfigurasi diterapkan:


 > npx discovery Load config from .discoveryrc.js Init single model default Define default routes ... OK Cache: DISABLED Init common routes ... OK Server listen on http://localhost:8123 

Seperti yang Anda lihat, sekarang file yang kami buat digunakan. Dan model default yang dijelaskan oleh kami diterapkan (Discovery dapat bekerja dalam mode banyak model, kami akan berbicara tentang fitur ini dalam manual berikut). Mari kita lihat apa yang telah berubah di browser:


Dengan konfigurasi dasar


Apa yang bisa dilihat di sini:


  • name digunakan sebagai judul halaman;
  • hasil pemanggilan metode data ditampilkan sebagai konten utama halaman.

Catatan: metode data harus mengembalikan data atau Janji, yang memutuskan untuk data.

Pengaturan dasar dibuat, Anda dapat melanjutkan.


Konteks


Mari kita lihat halaman laporan khusus (klik Make report ):


Halaman laporan


Pada pandangan pertama, ini tidak jauh berbeda dari halaman awal ... Tetapi di sini Anda dapat mengubah segalanya! Misalnya, kami dapat dengan mudah membuat ulang tampilan halaman awal:


Rekreasi halaman awal


Perhatikan bagaimana tajuk didefinisikan: "h1:#.name" . Ini adalah tajuk tingkat pertama dengan isi #.name , yang merupakan permintaan Jora . # mengacu pada konteks permintaan. Untuk melihat isinya, cukup masukkan # di editor kueri dan gunakan tampilan default:


Nilai konteks


Sekarang Anda tahu cara mendapatkan ID halaman saat ini, parameternya, dan nilai berguna lainnya.


Pengumpulan data


Sekarang kami menggunakan rintisan dalam proyek bukan data nyata, tetapi kami membutuhkan data nyata. Untuk melakukan ini, buat modul dan ubah nilai data dalam konfigurasi (omong-omong, setelah perubahan ini tidak perlu me-restart server):


 module.exports = { name: 'Node modules structure', data: require('./collect-node-modules-data') }; 

Isi dari collect-node-modules-data.js :


 const path = require('path'); const scanFs = require('@discoveryjs/scan-fs'); module.exports = function() { const packages = []; return scanFs({ include: ['node_modules'], rules: [{ test: /\/package.json$/, extract: (file, content) => { const pkg = JSON.parse(content); if (pkg.name && pkg.version) { packages.push({ name: pkg.name, version: pkg.version, path: path.dirname(file.filename), dependencies: pkg.dependencies }); } } }] }).then(() => packages); }; 

Saya menggunakan paket @discoveryjs/scan-fs , yang menyederhanakan pemindaian sistem file. Contoh menggunakan paket dijelaskan dalam readme-nya, saya mengambil contoh ini sebagai dasar dan diselesaikan sesuai kebutuhan. Sekarang kami memiliki beberapa informasi tentang isi node_modules :


Data dikumpulkan


Apa yang kamu butuhkan! Dan terlepas dari kenyataan bahwa ini adalah JSON biasa, kita sudah dapat menganalisisnya dan menarik beberapa kesimpulan. Misalnya, menggunakan popup struktur data, Anda dapat mengetahui jumlah paket dan mencari tahu berapa banyak dari mereka yang memiliki lebih dari satu contoh fisik (karena perbedaan dalam versi atau masalah dengan deduplikasi mereka).


Penelitian struktur data


Terlepas dari kenyataan bahwa kami sudah memiliki beberapa data, kami membutuhkan lebih banyak detail. Sebagai contoh, akan menyenangkan untuk mengetahui instance fisik mana yang menyelesaikan setiap dependensi yang dideklarasikan dari modul tertentu. Namun, upaya meningkatkan ekstraksi data berada di luar cakupan panduan ini. Oleh karena itu, kami akan menggantinya dengan paket @discoveryjs/node-modules (yang juga didasarkan pada @discoveryjs/scan-fs ) untuk mengambil data dan mendapatkan detail yang diperlukan tentang paket. Akibatnya, collect-node-modules-data.js sangat disederhanakan:


 const fetchNodeModules = require('@discoveryjs/node-modules'); module.exports = function() { return fetchNodeModules(); }; 

Sekarang informasi tentang node_modules terlihat seperti ini:


Struktur data baru


Script persiapan


Seperti yang mungkin Anda perhatikan, beberapa objek yang menggambarkan paket berisi deps - daftar dependensi. Setiap ketergantungan memiliki bidang yang resolved yang nilainya merupakan referensi ke instance fisik paket. Tautan semacam itu adalah nilai path salah satu paket, unik. Untuk menyelesaikan tautan ke paket, Anda perlu menggunakan kode tambahan (misalnya, #.data.pick(<path=resolved>) ). Dan tentu saja, akan jauh lebih nyaman jika tautan seperti itu sudah dipecahkan menjadi referensi objek.


Sayangnya, pada tahap pengumpulan data, kami tidak dapat menyelesaikan tautan, karena ini akan menyebabkan koneksi sirkuler, yang akan menciptakan masalah mentransfer data tersebut dalam bentuk JSON. Namun, ada solusinya: ini naskah prepare khusus. Ini didefinisikan dalam konfigurasi dan dipanggil setiap kali data baru ditugaskan ke instance Discovery. Mari kita mulai dengan konfigurasi:


 module.exports = { ... prepare: __dirname + '/prepare.js', // :   ,    ... }; 

Tentukan prepare.js :


 discovery.setPrepare(function(data) { //  -  data /   discovery }); 

Dalam modul ini, kami mendefinisikan fungsi prepare untuk instance Discovery. Fungsi ini dipanggil setiap kali sebelum menerapkan data ke instance Discovery. Ini adalah tempat yang baik untuk memungkinkan nilai dalam referensi objek:


 discovery.setPrepare(function(data) { const packageIndex = data.reduce((map, pkg) => map.set(pkg.path, pkg), new Map()); data.forEach(pkg => pkg.deps.forEach(dep => dep.resolved = packageIndex.get(dep.resolved) ) ); }); 

Di sini kami telah membuat indeks paket di mana kuncinya adalah nilai path paket (unik). Kemudian kita pergi melalui semua paket dan dependensinya, dan dalam dependensi kita mengganti nilai yang resolved dengan referensi ke objek paket. Hasil:


Deps yang diterbitkan.  Terselesaikan


Sekarang jauh lebih mudah untuk membuat query grafik dependensi. Ini adalah bagaimana Anda bisa mendapatkan sekelompok dependensi (mis. Dependensi, dependensi dependensi, dll.) Untuk paket tertentu:


Contoh Cluster Ketergantungan


Sebuah kisah sukses yang tak terduga: ketika mempelajari data selama penulisan manual, saya menemukan masalah di @discoveryjs/cli (menggunakan kueri .[deps.[not resolved]] ), yang memiliki kesalahan ketik pada peerDependencies. Masalahnya segera diperbaiki . Kasing merupakan contoh yang baik tentang bagaimana alat tersebut membantu.

Mungkin sudah waktunya untuk menunjukkan pada halaman awal beberapa nomor dan paket dengan memakan waktu.


Kustomisasi Halaman Awal


Pertama, kita perlu membuat modul halaman, misalnya, pages/default.js . Kami menggunakan default , karena ini adalah pengidentifikasi untuk halaman awal, yang dapat kami timpa (dalam Discovery.js, Anda dapat menimpa banyak). Mari kita mulai dengan sesuatu yang sederhana, misalnya:


 discovery.page.define('default', [ 'h1:#.name', 'text:"Hello world!"' ]); 

Sekarang dalam konfigurasi Anda perlu menghubungkan modul halaman:


 module.exports = { name: 'Node modules structure', data: require('./collect-node-modules-data'), view: { assets: [ 'pages/default.js' //     ] } }; 

Periksa di browser:


Halaman Awal yang Digantikan


Itu berhasil!


Sekarang mari kita cari beberapa counter. Untuk melakukan ini, buat perubahan pada pages/default.js :


 discovery.page.define('default', [ 'h1:#.name', { view: 'inline-list', item: 'indicator', data: `[ { label: 'Package entries', value: size() }, { label: 'Unique packages', value: name.size() }, { label: 'Dup packages', value: group(<name>).[value.size() > 1].size() } ]` } ]); 

Di sini kita mendefinisikan daftar indikator inline. Nilai data adalah kueri Jora yang membuat array rekaman. Daftar paket (akar data) digunakan sebagai dasar untuk kueri, jadi kami mendapatkan panjang daftar ( size() ), jumlah nama paket unik ( name.size() ) dan jumlah nama paket yang memiliki duplikat ( group(<name>).[value.size() > 1].size() ).


Tambahkan indikator ke halaman awal


Tidak buruk. Namun demikian, akan lebih baik untuk memiliki, selain angka, tautan ke sampel yang sesuai:


 discovery.page.define('default', [ 'h1:#.name', { view: 'inline-list', data: [ { label: 'Package entries', value: '' }, { label: 'Unique packages', value: 'name' }, { label: 'Dup packages', value: 'group(<name>).[value.size() > 1]' } ], item: `indicator:{ label, value: value.query(#.data, #).size(), href: pageLink('report', { query: value, title: label }) }` } ]); 

Pertama-tama, kami mengubah nilai data , sekarang ini adalah array reguler dengan beberapa objek. Selain itu, metode size() telah dihapus dari permintaan nilai.


Selain itu, subquery telah ditambahkan ke tampilan indicator . Jenis kueri ini membuat objek baru untuk setiap elemen di mana value dan href dihitung. Untuk value , kueri dieksekusi menggunakan metode query() , yang datanya ditransfer dari konteks, dan kemudian metode size() diterapkan pada hasil kueri. Untuk href , metode pageLink() digunakan, yang menghasilkan tautan ke halaman laporan dengan permintaan dan tajuk tertentu. Setelah semua perubahan ini, indikator menjadi dapat diklik (perhatikan bahwa nilainya telah menjadi biru) dan lebih fungsional.


Indikator yang dapat diklik


Untuk membuat halaman awal lebih bermanfaat, tambahkan tabel dengan paket yang memiliki duplikat.


 discovery.page.define('default', [ // ...      'h2:"Packages with more than one physical instance"', { view: 'table', data: ` group(<name>) .[value.size() > 1] .sort(<value.size()>) .reverse() `, cols: [ { header: 'Name', content: 'text:key' }, { header: 'Version & Location', content: { view: 'list', data: 'value.sort(<version>)', item: [ 'badge:version', 'text:path' ] } } ] } ]); 

Tabel ini menggunakan data yang sama dengan indikator Dup packages . Daftar paket diurutkan berdasarkan ukuran grup dalam urutan terbalik. Sisa pengaturan terkait dengan kolom (omong-omong, biasanya mereka tidak perlu disetel). Untuk kolom Version & Location , kami mendefinisikan daftar bersarang (diurutkan berdasarkan versi), di mana setiap elemen adalah pasangan nomor versi dan jalur ke instance.


Contoh tabel dengan paket yang memiliki lebih dari satu contoh fisik


Halaman Paket


Sekarang kami hanya memiliki gambaran umum tentang paket. Tetapi akan bermanfaat untuk memiliki halaman dengan detail tentang paket tertentu. Untuk melakukan ini, buat pages/package.js modul baru pages/package.js dan tentukan halaman baru:


 discovery.page.define('package', { view: 'context', data: `{ name: #.id, instances: .[name = #.id] }`, content: [ 'h1:name', 'table:instances' ] }); 

Dalam modul ini, kami mendefinisikan halaman dengan package pengenal. Komponen context digunakan sebagai representasi awal. Ini adalah komponen non-visual yang membantu Anda menentukan data untuk pemetaan bersarang. Perhatikan bahwa kami menggunakan #.id untuk mendapatkan nama paket, yang diambil dari URL seperti ini http://localhost:8123/#package:{id} .


Jangan lupa untuk memasukkan modul baru dalam konfigurasi:


 module.exports = { ... view: { assets: [ 'pages/default.js', 'pages/package.js' //   ] } }; 

Hasil dalam browser:


Contoh Halaman Paket


Tidak terlalu mengesankan, tetapi untuk saat ini. Kami akan membuat pemetaan yang lebih kompleks di manual selanjutnya.


Panel samping


Karena kita sudah memiliki halaman paket, alangkah baiknya memiliki daftar semua paket. Untuk melakukan ini, Anda dapat menentukan tampilan khusus - sidebar , yang ditampilkan jika sudah ditentukan (tidak ditentukan secara default). Buat views/sidebar.js modul baru views/sidebar.js :


 discovery.view.define('sidebar', { view: 'list', data: 'name.sort()', item: 'link:{ text: $, href: pageLink("package") }' }); 

Sekarang kami memiliki daftar semua paket:


Bilah sisi ditambahkan


Itu terlihat bagus. Tetapi dengan filter itu akan lebih baik. Kami memperluas definisi sidebar :


 discovery.view.define('sidebar', { view: 'content-filter', content: { view: 'list', data: 'name.[no #.filter or $~=#.filter].sort()', item: { view: 'link', data: '{ text: $, href: pageLink("package"), match: #.filter }', content: 'text-match' } } }); 

Di sini kami membungkus daftar dalam komponen content-filter yang mengubah nilai input dalam bidang input ke ekspresi reguler (atau null jika bidang kosong) dan menyimpannya sebagai nilai filter dalam konteks (nama dapat diubah dengan opsi name ). Juga, untuk memfilter data untuk daftar, kami menggunakan #.filter . Akhirnya, kami menerapkan pemetaan tautan untuk menyorot bagian yang cocok dengan text-match . Hasil:


Filter daftar


Jika Anda tidak menyukai desain default, Anda dapat menyesuaikan gaya sesuai keinginan. Katakanlah Anda ingin mengubah lebar bilah sisi, untuk ini Anda perlu membuat file gaya (katakanlah, views/sidebar.css ):


 .discovery-sidebar { width: 300px; } 

Dan tambahkan tautan ke file ini dalam konfigurasi, serta ke modul JavaScript:


 module.exports = { ... view: { assets: [ ... 'views/sidebar.css', //  assets    *.css  'views/sidebar.js' ] } }; 

Tautan Otomatis


Bab terakhir dari panduan ini dikhususkan untuk tautan. Sebelumnya, menggunakan metode pageLink() , kami membuat tautan ke halaman paket. Namun selain tautan, Anda juga harus mengatur teks tautan. Tetapi bagaimana kita membuatnya lebih mudah?


Untuk menyederhanakan pekerjaan tautan, kita perlu mendefinisikan aturan untuk menghasilkan tautan. Ini paling baik dilakukan dalam skrip prepare :


 discovery.setPrepare(function(data) { ... const packageIndex = data.reduce( (map, item) => map .set(item, item) // key is item itself .set(item.name, item), // and `name` value new Map() ); discovery.addEntityResolver(value => { value = packageIndex.get(value) || packageIndex.get(value.name); if (value) { return { type: 'package', id: value.name, name: value.name }; } }); }); 

Kami menambahkan peta baru (indeks) paket dan menggunakannya untuk resolver entitas. Penyelesai entitas mencoba, jika mungkin, untuk mengubah nilai yang diteruskan ke entitas descriptor. Deskriptor berisi:


  • type - tipe entitas
  • id - referensi unik ke instance entitas yang digunakan dalam tautan sebagai ID
  • name - digunakan sebagai teks tautan

Terakhir, Anda perlu menetapkan jenis ini ke halaman tertentu (tautannya mengarah ke suatu tempat, bukan?).


 discovery.page.define('package', { ... }, { resolveLink: 'package' //    `package`    }); 

Konsekuensi pertama dari perubahan ini adalah bahwa beberapa nilai dalam tampilan struct sekarang ditandai dengan tautan ke halaman paket:


Tautan otomatis dalam struct


Dan sekarang Anda juga dapat menerapkan komponen auto-link ke objek atau nama paket:


Menggunakan tautan otomatis


Dan, sebagai contoh, Anda dapat sedikit mengolah ulang bilah samping:


  //   item: { view: 'link', data: '{ text: $, href: pageLink("package"), match: #.filter }', content: 'text-match' }, //   `auto-link` item: { view: 'auto-link', content: 'text-match:{ text, match: #.filter }' } 

Kesimpulan


Anda sekarang memiliki pemahaman dasar tentang konsep-konsep kunci dari Discovery.js . Dalam panduan berikut kami akan melihat lebih dekat topik yang dibahas.


Anda dapat melihat seluruh kode sumber panduan dalam repositori di GitHub atau mencoba cara kerjanya secara online .


Ikuti @js_discovery di Twitter untuk mengikuti berita terbaru!

Source: https://habr.com/ru/post/id469115/


All Articles