Zephyrnet-logo

Uw eigen NPM-pakket maken

Datum:

Coverafbeelding voor Uw eigen NPM-pakket maken

NPM staat voor Node-pakketbeheerder, een softwareregister voor open source-software waar gebruikers pakketten kunnen publiceren voor zowel openbaar als privégebruik.

Pakketten beheren binnen een npm-pakket

Een npm-pakket is normaal gesproken een basistoepassing die andere pakketten gebruikt om basisfunctionaliteiten te beheren en te gebruiken. Maar normaal gesproken gebruiken we garen of npm om deze pakketten te installeren, maar bij het maken van een npm-pakket hebben we een globale manier nodig om pakketten voor het hele project op te slaan en te beheren. Dus hiervoor heb ik LERNA gebruikt, de officiële documentatie voor lerna is te vinden hier.

Dit is de basismapstructuur voor het beheer van lerna-projecten lerna mappenstructuur

De map heeft de volgende afhankelijkheden: -

  • cli (beheer van de opdrachtregelinterface)
  • local-API (de back-end-API gebouwd op Express)
  • local-client (de UI-interface gebruikt React, redux en bulma voor styling)

Laten we eerst eens kijken naar CLI

Voor CLI gebruikte ik een pakket met de naam commander waarin u code schrijft om uw opdrachtregelinterface te beschrijven. Commander zorgt voor het ontleden van de argumenten in opties en opdrachtargumenten, geeft gebruiksfouten weer voor problemen en implementeert een helpsysteem voor niet-herkende opties, het geeft een fout weer.

De officiële documentatie voor de commandant is te vinden Here.

De commandant neemt een commando en enkele opties in zich op, in dit geval is het commando serve en de optie is een poortnummer waar dit programma op draait, standaard 4005.

const serveCommand = new Command() .command('serve [filename]') .description('Open a file for editing') .option('-p --port <number>', 'port to run server on', '4005') .action(async (filename = 'notebook.js', options: { port: string }) => { try { const dir = path.join(process.cwd(), path.dirname(filename)); await serve( parseInt(options.port), path.basename(filename), dir, !isProduction ); console.log( `Opened ${filename}. Navigate to http://localhost:${options.port} to edit the file.` ); } catch (error: any) { if (error.code === 'EADDRINUSE') { console.error('Port is already in use please try another port'); } else { console.log(error.message); } process.exit(1); } });

Daarnaast worden de volgende afhankelijkheden ook gebruikt in het cli-pakket

cli-pakket

In de lokale api-directory zijn alle routes gedefinieerd, het heeft in feite twee routes: -

  • Een get-route naar /cells (Dit eindpunt retourneert de bestaande celgegevens uit het notebookbestand)
router.get('/cells', async (req, res) => { try { const result = await fs.readFile(fullPath, { encoding: 'utf-8' }); res.send(JSON.parse(result)); } catch (error: any) { if (error.code === 'ENOENT') { await fs.writeFile(fullPath, '[]', 'utf-8'); res.send([]); } else { throw error; } } });

In eerste instantie proberen we de bestaande inhoud van het bestand te lezen met behulp van de ingebouwde module van het bestandssysteem (fs) en aangezien de gegevens in JSON-indeling zijn, analyseren we het en sturen we het terug.

Door de volledige code in een try-catch-blok te wikkelen, is het gemakkelijker om fouten te verzenden in plaats van de app te laten crashen.

  • Een postroute naar /cells (Dit eindpunt verzendt de bestaande celgegevens die moeten worden opgeslagen in een notebookbestand)
router.post('/cells', async (req, res) => { const { cells }: { cells: Cell[] } = req.body; await fs.writeFile(fullPath, JSON.stringify(cells), 'utf-8'); res.send({ status: 'ok' }); });

Evenzo krijgen we in de postroute de gegevens van de client die deze converteren naar een JSON-string en deze terugschrijven met dezelfde bestandssysteem(fs)-module.

U kunt meer vinden over FS-modules hier.

Eindelijk komt de clientmodule die is gebouwd met behulp van React, redux, typescript, bulma en monaco-editor.

Hiervoor waren de belangrijkste uitdagingen:

  • Een markdown-editor bouwen
  • Een oplossing bouwen voor het online schrijven en compileren van code in de browser zelf.
  • Een bundel bouwen voor compilatie.

Voor de markdown-editor die ik uiteindelijk heb gebruikt @uiw/react-md-editor.

import { useState, useEffect, useRef } from 'react';
import MDEditor from '@uiw/react-md-editor';
import './css/text-editor.css';
import { Cell } from '../state';
import { useActions } from '../hooks/use-actions'; interface TextEditorProps { cell: Cell;
} const TextEditor: React.FC<TextEditorProps> = ({ cell }) => { const [editing, setEditing] = useState(false); const ref = useRef<HTMLDivElement | null>(null); const { updateCell } = useActions(); useEffect(() => { const listener = (event: MouseEvent) => { if ( ref.current && event.target && ref.current.contains(event.target as Node) ) return; setEditing(false); }; document.addEventListener('click', listener, { capture: true }); return () => { document.removeEventListener('click', listener, { capture: true }); }; }, []); if (editing) { return ( <div className="text-editor" ref={ref}> <MDEditor value={cell.content} onChange={(v) => updateCell(cell.id, v || '')} /> </div> ); } return ( <div className="text-editor card" onClick={() => setEditing(true)}> <div className="card-content"> <MDEditor.Markdown source={cell.content || 'Click to edit'} /> </div> </div> );
}; export default TextEditor;

Om meer te lezen over @uiw/react-md-editor je kunt gaan hier.

Voor het online schrijven en compileren van code had ik een code-editor nodig die eruitziet en aanvoelt als VS-Code en dus eindigde ik met de monaco-editor die door Microsoft zelf is gemaakt en ook VS-code aanstuurt.

Dit is de configuratie die ik heb gebruikt voor mijn editorcomponent: -

<MonacoEditor editorDidMount={onEditorMount} value={initialValue} height="100%" language="javascript" theme="dark" options={{ wordWrap: 'on', matchBrackets: 'always', minimap: { enabled: false }, showUnused: false, folding: false, lineNumbersMinChars: 3, fontSize: 18, scrollBeyondLastLine: false, automaticLayout: true, }}
/>

Nu na het maken van de editor waren er nog 2 problemen: -

  1. De code was niet correct geformatteerd.
  2. En er waren enkele opvallende problemen.

Om code-opmaak te repareren, heb ik een knop gemaakt die het mooiere pakket aanroept om code op te maken.

const onFormatClick = () => { const unFormatted = editorRef.current.getModel().getValue(); const formatted = prettier .format(unFormatted, { parser: 'babel', plugins: [parser], useTabs: false, semi: true, singleQuote: true, }) .replace(/n$/, ''); editorRef.current.setValue(formatted); }; <button onClick={onFormatClick}> Format
</button>

Vervolgens gebruikte ik voor code-accentuering jscodeshift en monaco-jsx-highlighteren creëerde een mount-component die liep toen de editor aankoppelde: -

const onEditorMount: EditorDidMount = (getValue, monacoEditor) => { editorRef.current = monacoEditor; monacoEditor.onDidChangeModelContent(() => { onChange(getValue()); }); monacoEditor.getModel()?.updateOptions({ tabSize: 2 }); const highlighter = new Highlighter( // @ts-ignore window.monaco, codeshift, monacoEditor ); highlighter.highLightOnDidChangeModelContent( () => {}, () => {}, undefined, () => {} ); };

Dan komt het belangrijkste deel De bundelaar :-

Voor bundeling is de basis use-case dat we de code moeten ophalen, deze moeten compileren en vervolgens de uitvoer moeten tonen. Wat nu als een gebruiker enkele pakketten uit het npm-register importeert? Om die reden zouden we een bundelprogramma nodig hebben en in mijn geval heb ik unpkg gebruikt en een bundelservice gemaakt.

import * as esbuild from 'esbuild-wasm';
import { fetchPlugin } from './plugins/fetch-plugin';
import { unpkgPathPlugin } from './plugins/unpkg-path-plugin'; let service: esbuild.Service;
const bundle = async (rawCode: string) => { if (!service) { service = await esbuild.startService({ worker: true, wasmURL: 'https://unpkg.com/esbuild-wasm@0.8.27/esbuild.wasm', }); } try { const result = await service.build({ entryPoints: ['index.js'], bundle: true, write: false, plugins: [unpkgPathPlugin(), fetchPlugin(rawCode)], define: { 'process.env.NODE_ENV': '"production"', global: 'window', }, jsxFactory: '_React.createElement', jsxFragment: '_React.Fragment', }); return { code: result.outputFiles[0].text, err: '' }; } catch (err) { return { code: '', err: (err as Error).message }; }
}; export default bundle;

Putting het allemaal samen

Hierna is het tijd om het in het npm-register te implementeren, daarvoor zouden we een npm-account moeten maken dat vrijwel ongecompliceerd is en gemakkelijk kan worden gedaan door naar npm te gaan van de en aanmelden.

Nu moeten we enkele wijzigingen aanbrengen in ons bestand package.json.

We moeten de belangrijkste, typen (als het een typescriptbestand is) en licentie (meestal MIT voor OSS) toevoegen

eerste stap

Voeg nu de publishConfig toe om openbaar of privé te zijn en de invoermap van waaruit npm dient.
step2

Dat is het, je bent klaar om te gaan...
Here is de volledige broncode voor het project.

Bekijk het en laat een ster achter..

spot_img

Laatste intelligentie

spot_img