esmpack, transform javascript files to es module that can be imported by browser.
- As Global
npm i -g @ibyar/esmpack
yarn global add @ibyar/esmpack
- As Developer Dependencies
npm i --save-dev @ibyar/esmpack
yarn add --dev @ibyar/esmpack
can load config from js file (es module), or json file, if not
Version 0.1.9
Usage: esmpack [config path] [options]
if no config file in the commend will try to search for file names
'esmpack.js', 'esmpack.mjs' and 'esmpack.json'.
Examples:
esmpack
esmpack esmpack.js
esmpack esmpack.json -w -d
esmpack -v
esmpack --help
Options:
--prod build for production, minify modules, css, etc...
-d --debug output debug messages on internal operations
-s --silent don't print any thing
-w --watch watch files for change
-h --help print help message
-v --version output the version number
js config file, proved a way to connect esmpack
with any plugin, the built-in plugins can be called by its name css, html, json, text, image, audio
.
- The built in plugin, will try to fetch the resource with
fetch
request. - The Built-in plugin provide 3 different information about the resource file:
- 1-
value
: content itself, its type controlled by the plugin, import.json
file will return json object, import.txt
,.css
and.html
will return a string object. - 2-
url
: a url for the resources. - 3-
promise
: the promise to listen, to be notified with the fetch result.
declare module '*.html' {
export default value;
export const value: string;
export const url: string;
export const promise: Promise<string>;
}
import htmlFile, {url as appUrl, promise as appPromise} from './app-root.html';
// will be transformed to
let htmlFile;
const appUrl;
const appPromise;
if didn't provide a config path to the cli, will search for config file in the process.cwd()
for file name esmpack.config.js
if not found, then search for esmpack.config.mjs
, then esmpack.config.json
, if non of them found, will exits with error code.
Could be a js module file with default export as JSConfig Interface
object. in that case make sure the package.json
is marked as module package, {"type": "module"}
. this options is useful for nodejs when (importing a non .mjs
) OR ( import .js
) file.
let pdfHandler = (importSyntax, relativeFilePath) => {
/**
* write plugin for pdf files
* convert the pdf file to js module with default export as content of the pdf as `string`.
*/
return {action: 'module'};
};
const config = {
moduleResolution: 'relative',
outDir: 'build/web_modules/',
pathMap: { 'src': 'dist' },
src: {
files: [],
include: ['./dist/**/*.js'],
exclude: []
},
resources: {
files: [],
include: ['./src/**/*.*'],
exclude: ['./src/**/*.{js,ts,tsx}']
},
plugins: [
'css',
'html',
'image',
'json',
'txt',
{ text: /\.pdf$/g, handler: pdfHandler },
{ test: /\.xml$/g, moduleType: 'text'},
]
};
export default config;
- for now the built-in plugin the oly pne supported.
{
"moduleResolution": "relative",
"outDir": "public/web_modules/",
"pathMap": {
"src": "dist"
},
"src": {
"files": [],
"include": [
"./dist/**/*.js"
],
"exclude": []
},
"resources": {
"files": [],
"include": [
"./src/**/*.*"
],
"exclude": [
"./src/**/*.{js,ts,tsx}"
]
},
"plugins": [
"css",
"html",
"json",
"text",
"image"
]
}
export interface ESMConfig {
/**
* build dir
*/
outDir: string;
/**
* location of input files
*/
src: SourceInput;
/**
* resource dir to copy to and look/resolve other import
* module '.css', '.html' '.map' files.
*/
resources: SourceInput;
pathMap: { [key: string]: string };
/**
* preferred extension for build files '.js'.
*
* for web use ".js" if your server can't handle ".mjs"
* as MIME type "text/javascript" and Content-Type: text/javascript.
*
* for nodejs support ".mjs" out of box starting with version .
* or As of Node 13, you can use ext: ".js"
* and can trigger support by setting "type": "module", in package.json
*/
extension: ModuleExtension;
/**
* `relative`: convert import statement to be relative path to its dir
* ```
* npm_modules/@models/people/model/person.js
* export class Person {....}
*
* npm_modules/@models/people/index.js
* export { Person } from './model/person';
*
* dist/views/person-component.js
* import { Person } from '@models/people';
* ```
* will be
*
* ```
* {outDir}/@models/people/model/person.js
* export class Person {....}
*
* {outDir}/@models/people/index.js
* export { Person } from './model/person.js';
*
* {outDir}/dist/views/person-component.js
* import { Person } from '../../@models/people/index.js';
* ```
*
* `static`: resolve import to be static path, 'baseUrl' should be used
* the new path will ``` 'baseUrl' / 'pkg_name' / 'file path inside outDir'```
* assume baseUrl = '/js/esm'
* the full url if http://localhost/js/esm
*
* ```
* {outDir}/@models/people/model/person.js
* export class Person {....}
*
* {outDir}/@models/people/index.js
* export { Person } from '/js/esm/@models/model/person.js';
*
* {outDir}/{project_pkg_name}/views/person-component.js
* import { Person } from '/js/esm/@models/people/index.js';
* ```
*
* `flat`: all modules will be in 'outDir' folder,
* files name will be combination from package name and module path, joined by '.'
*
* ```
* {outDir}/@models.people.model.person.js
* export class Person {....}
*
* {outDir}/@models.people.index.js
* export { Person } from './@models.people.model.person.js';
*
* {outDir}/{project_pkg_name}.views.person-component.js
* import { Person } from './@models.people.index.js';
* ```
*
*/
moduleResolution: 'relative' | 'static' | 'flat';
/**
* Base directory to resolve 'static' module names.
*/
baseUrl: string;
/**
* the output of this transformation shall
* scan 'all' js files in 'files' and 'include' and 'npm_modules' package.
*
* or just 'follow' import statements in js module files for this workspace.
*
* default to 'all'
*/
workspaceResolution: 'all' | 'follow';
/**
* list of plugins to handle non-js supported files,
*
* built-in plugin is ['html', 'css', 'txt' ,'json', 'image files, png, jpg, ico,...']
*/
plugins: PluginHandler[];
/**
* build for production
*/
prod: boolean,
}
type ConfigPlugin = string| { text: /\.pdf$/g, handler: (importSyntax: ImportSyntax, relativeFilePath: string): PluginAction };
export interface JSConfig {
outDir: string;
src?: SourceInput;
resources?: SourceInput;
pathMap?: { [key: string]: string };
extension?: ".js" | ".mjs";
moduleResolution?: 'relative' | 'static' | 'flat';
baseUrl?: string;
workspaceResolution?: 'all' | 'follow';
plugins?: ConfigPlugin[];
}
- for now all built-in plugin intercepting only
import
statement,export
statement not implemented yet.
class PluginAction { action: 'replace' | 'fetch' | 'module', inline?: string }
let pdfHandler = (importSyntax: ImportSyntax, relativeFilePath: string): PluginAction => {
// write plugin for pdf files
return {action: 'module'};
};
// provide a new plugin with the help of built-in plugin behavior.
let mdPluginHandler = new Plugin('text');
let pngImage = new Plugin('objectURL');
let config = {
...
plugins: [
'json',
{ test: /\.pdf$/g, handler: pdfHandler },
{ test: /\.xml$/g, moduleType: 'text' },
{ test: /\.md$/g, handler: mdPluginHandler },
{ test: /\.png$/g, handler: pngImage }
]
}
replace:
replace import statement with code.
fetch
: replace the import statement with a fetch request code, from the inline.
module
: convert the resource file to a js module, and import its default.
inline
: hold code to be replaced with the import statement.
moduleType
value could be: text
, json
, blob
, arrayBuffer
, file
, uint8
, objectURL
, base64
and dataBase64
.
app/file.html
<h1> Hello from HTML</h1>
app/main-module.js
import htmlContent, {url, promise, value} from './file1.html';
console.log(htmlContent); // print undefined
console.log(value); // print undefined
console.log(url); // print 'http://site-url/{outDir}/app/file1.html'
promise.then(content => {
console.log(htmlContent); // print '<h1> Hello from HTML</h1>'
console.log(value); // print '<h1> Hello from HTML</h1>'
console.log(content); // print '<h1> Hello from HTML</h1>'
console.log(content == htmlContent); // print true
console.log(content == html); // print true, the same object
})
.catch(reason => console.error('failed loading html file'));
@aurora/esmpack
support operator as
for renaming the default exported names in the wildcard
import json, {promise as jsonPromise} from './file.json';
import text, {promise as textPromise} from './file.txt';
import {url as imageUrl} from './image.jpg';
file1Promise.then(content => {
....
});
textPromise.then(content => {
....
});
@aurora/esmpack
support operator export
from a non-js file.
/// <reference types="@ibyar/types" />
export * from './file.json';
export * from './file.txt';
/**will throw error at runtime,
* keep aware of renaming wildcard binding names, so not conflict with each other.
* Consider explicitly re-exporting to resolve the ambiguity
*/
export {url as imageUrl, jpg as imageObject} from './image.jpg';
export * as image from './image.jpg';
- set extension of module file to '.js' or '.mjs', currently, doesn't override filename.
- moduleResolution
static
andflat
: currentlyrelative
is supported