UNPKG

@kawix/core

Version:

The next generation module loader for nodejs

431 lines (277 loc) 10.5 kB
## Install Refer to [INSTALL.md](./INSTALL.md) for instalation instructions. ```@kawix/core``` has its own installer and executable files, but you can still use directly with node installing this package with npm or yarn. Is known to work with node 10+ ## Get started ```bash > kwcore "https://kwx.kodhe.com/x/v/0.7.3/core/example/http" ``` If you run the previous example, you will see and HTTP server example running. Look the [https://kwx.kodhe.com/x/core/example/http](https://kwx.kodhe.com/x/core/example/http) content ```javascript import httpServer from './http-async' var server= new httpServer() server.listen(8081) console.info("Open in browser: 127.0.0.1:8081") var handle= async function(){ var conn while(conn = await server.acceptAsync()){ conn.res.write("Hello world! from URL: " + conn.req.url) conn.res.end() } console.info("Http server stopped") } handle() ``` Yes, you can import from urls or files, and inside *kawix required files* you can import relative urls or file in the normal way using **import** and access to all **async/await** features ```javascript // require a url import httpServer from 'https://kwx.kodhe.com/x/v/0.7.3/core/example/http-async' // require a file import test from './test' ``` ## Programatically You can run all scripts (including the examples here) using **kwcore** executable installed with npm. If you want run programatically you need include a file like this and execute with node.js ```javascript var Kawix= require("@kawix/core") // Kawix.KModule.import returns a promise Kawix.KModule.import("https://kwx.kodhe.com/x/v/0.7.3/core/example/http", { // allow find path relative to this module parent: module }) ``` ## Why @kawix/core instead of esm * Imports are async * Imports from http:// and https:// urls * Imports dynamically npm packages (npm://package@version) * Cache compilation secure between processes * Support Typescript on the fly ## Runtime options * Flag ```--force``` allows you to ignore the cache of your remote files and recompile * Environment variable ```KAWIX_CACHE_DIR``` allows you to specify the folder when compiled/cached files are saved. Is good for example if you want flexibility in development, but in production want include all dependencies (remote,npm) in the distributable ## Features ### 1. Imports are async, imports from URL Create an **.js** with following content ```javascript import httpServer from 'https://kwx.kodhe.com/xv/0.7.3/core/example/http-async' var server= new httpServer() server.listen(8081) console.info("Open in browser: 127.0.0.1:8081") var handle= async function(){ var conn while(conn = await server.acceptAsync()){ conn.res.write("Hello world! from URL: " + conn.req.url) conn.res.end() } console.info("Http server stopped") } handle() ``` **IMPORTANT:** 1. ```@kawix/core``` doesn't resolve dependencies like node.js. For example, this code ```import xx from 'request'``` will fallback to internal *node.js* resolution method. The idea of ```@kawix/core``` is be simplier, always async, and inspired in *deno* allow imports from absolute, relative and URL paths. Also, like ```@kawix/core``` doesn't resolve dependencies like node.js, you cannot expect that importer search ```package.json``` or ```index.js``` if you import a folder. You should specify imports to files no folders. 2. ```@kawix/core``` execute imports before all other code. This means, that you should not call any function or make any operations between imports Think in this code: ```javascript // is builtin module, this line is untouched import fs from 'fs' // is processed by kawix import http from 'https://kwx.kodhe.com/x/core/example/http' // this is BAD, not recommended makeAnyOperation() import other from './other' export default function(){ // any operation return action() } ``` Will be translated to something like this (before transpiling): ```javascript import fs from 'fs' import http from 'cached_result_on_1' makeAnyOperation() import other from 'cached_result_on_2' export default function(){ // any operation return action() } // THIS will be executed before all file code var __kawi_async= async function(){ await KModule.import('https://kwx.kodhe.com/x/core/example/http', { // this ensure create a cache for be usable later with import uid: 'cached_result_on_1' }) await KModule.import('./other', { // this ensure create a cache for be usable later with import uid: 'cached_result_on_2' }) } ``` ### 2. Full Ecmascript 2018 (async/await) Create an .es6 or .js with following content and import with KModule ```javascript let value1= 7 ** 2 let arr= [10,23,4,NaN] console.log(value1) console.log(arr.includes(4)) let cars= { "BMW": 3, "Tesla": 2, "Toyota": 1 } let values= Object.values(cars) console.log(values) for(let [key,value] of Object.entries(cars)){ console.log(`key: ${key} value: ${value}`) } printInfo(1) function getUser(userId){ return new Promise((resolve)=>{ setTimeout(resolve.bind(null, 'John'), 1000) }) } async function printInfo(userId){ let user= await getUser(userId) console.log(user) } ``` ### 3. Support dynamic import Create an **.js** with following content ```javascript init() async function init(){ var mod= await import('https://kwx.kodhe.com/x/core/example/http-async') var httpServer= mod.default var server= new httpServer() server.listen(8081) console.info("Open in browser: 127.0.0.1:8081") var handle= async function(){ var conn while(conn = await server.acceptAsync()){ conn.res.write("Hello world! from URL: " + conn.req.url) conn.res.end() } console.info("Http server stopped") } handle() } ``` ### 4. Typescript support Create an .ts with following content and import with KModule ```typescript function greeter(person: string) { return "Hello, " + person; } function sleep(timeout?: number){ timeout= timeout || 0 return new Promise(function(resolve, reject){ setTimeout(resolve, timeout) }) } async function foo() { try { var val = greeter("World!"); console.log(val); console.log("Simulating async action... please wait 1 second") await sleep(1000) console.log("Finished") } catch(err) { console.log('Error: ', err.message); } } foo() ``` ### 5. Imports npm packages (dinamycally) Consider the following example ```javascript // this will download the npm module with dependencies, and make a local cache import 'npm://express@^4.16.4' import express from 'express' var app = express() app.get('/', function (req, res) { res.send('Hello World') }) app.listen(3000) console.log("Listening on 3000") ``` Test yourself from your terminal ```bash kwcore "https://kwx.kodhe.com/x/v/0.7.3/core/example/npmrequire/express" # take care that this project is in active development, if fails use --force for invalidate cache kwcore --force "https://kwx.kodhe.com/x/v/0.7.3/core/example/npmrequire/express" ``` **Use cases for dynamic loading?** * You need/want decrease or have zero *installation/update* effort (for example in a web service balanced on multiple servers) * You are testing development * You sell scripts to your clients *no developers* that have not idea how things works :v * You want portability * For little tools that you can make for your projects * For fun :) **When not use dynamic loading?** You can use always dynamic loading. However in Windows for modules with node-pregyp, you should prefer not using this. In Unix you can use dynamic loading for native modules too. **Is secure hot loading?** YES! @kawix/core uses internally ```yarn``` a lightweight and very compatible replacement for npm. Each module is isolated in individual folder, so you can expect same results across machines ### 6. Register language loaders ```@kawix/core``` allows register additional language loaders. For example, you can easily start to write *coffeescript* importing the loader available in github. Just see the example: Create a *test.js* file ```javascript // register coffeescript loader import 'https://kwx.kodhe.com/x/v/0.7.3/std/coffeescript/register' // You can now import .coffee files import {start} from 'https://kwx.kodhe.com/x/v/0.7.3/std/coffeescript/example.coffee' start() ``` Run with ```@kawix/core``` ```bash kwcore ./test ``` There is available a loader also for *cson* at [https://kwx.kodhe.com/x/v/0.7.3/std/coffeescript/cson/register](https://kwx.kodhe.com/x/v/0.7.3/std/coffeescript/cson/register) This is only an example you can create your own loaders ### 7. Generate a **single file bundle** from a **npm package** Yes, in some cases you may prefer have a single .js file instead of loading an entire npm package with a ton of modules. Consider this practical example * Create a *bundler.js* file ```javascript import Bundler from 'https://kwx.kodhe.com/x/v/0.7.3/std/package/bundle' import Registry from 'https://kwx.kodhe.com/x/v/0.7.3/std/package/registry' import Path from 'path' init() async function init(){ // caching a npm package: express 4.16.4 var reg= new Registry() var moduleinfo= await reg.resolve("express@^4.16.4") var path= moduleinfo.folder var outfile= Path.join(__dirname, "express.js") var bundler= new Bundler(path) // packageJsonSupport should be true for npm packages bundler.packageJsonSupport= true bundler.virtualName= `express` await bundler.writeToFile(outfile).bundle() } ``` * Execute with ```@kawix/core```, will generate an *express.js* file containing all the package. ```bash kwcore ./bundler.js ``` * Now you can import from other files. Create a ```server.js``` file: ```javascript import express from './express' var app = express() app.get('/', function (req, res) { res.send('Hello World') }) app.listen(3000) console.log("Listening on 3000") // go to browser and open 127.0.0.1:3000 ``` * Now execute the ```server.js``` file and go to browser: 127.0.0.1:3000 ```bash kwcore ./server.js Listening on 3000 ``` **Why generate a single file package?** * Faster installation times when you distribute scripts or packages * You want generate a distributable file from your package * You want create packages with zero or almost zero dependencies * You want avoid module version collision * Use in browser (coming soon) * If you are like me, that hates that one simple module downloads a tons of dependencies :v