=n;if(o&&i.edited&&i.end!==n)throw new Error("Cannot use replaced character "+n+" as slice end anchor.");var s=r===i?t-i.start:0,a=o?i.content.length+n-i.end:i.content.length;if(e+=i.content.slice(s,a),!i.outro||o&&i.end!==n||(e+=i.outro),o)break;i=i.next}return e},MagicString.prototype.snip=function(t,n){var e=this.clone();return e.remove(0,t),e.remove(n,e.original.length),e},MagicString.prototype._split=function(t){if(!this.byStart[t]&&!this.byEnd[t])for(var n=this.lastSearchedChunk,e=t>n.end;n;){if(n.contains(t))return this._splitChunk(n,t);n=e?this.byStart[n.end]:this.byEnd[n.start]}},MagicString.prototype._splitChunk=function(t,n){if(t.edited&&t.content.length){var e=getLocator(this.original)(n);throw new Error("Cannot split a chunk that has already been edited ("+e.line+":"+e.column+' – "'+t.original+'")')}var i=t.split(n);return this.byEnd[n]=t,this.byStart[n]=i,this.byEnd[i.end]=i,t===this.lastChunk&&(this.lastChunk=i),this.lastSearchedChunk=t,!0},MagicString.prototype.toString=function(){for(var t=this.intro,n=this.firstChunk;n;)t+=n.toString(),n=n.next;return t+this.outro},MagicString.prototype.isEmpty=function(){var t=this.firstChunk;do{if(t.intro.length&&t.intro.trim()||t.content.length&&t.content.trim()||t.outro.length&&t.outro.trim())return!1}while(t=t.next);return!0},MagicString.prototype.length=function(){var t=this.firstChunk,n=0;do{n+=t.intro.length+t.content.length+t.outro.length}while(t=t.next);return n},MagicString.prototype.trimLines=function(){return this.trim("[\\r\\n]")},MagicString.prototype.trim=function(t){return this.trimStart(t).trimEnd(t)},MagicString.prototype.trimEndAborted=function(t){var n=new RegExp((t||"\\s")+"+$");if(this.outro=this.outro.replace(n,""),this.outro.length)return!0;var e=this.lastChunk;do{var i=e.end,r=e.trimEnd(n);if(e.end!==i&&(this.lastChunk===e&&(this.lastChunk=e.next),this.byEnd[e.end]=e,this.byStart[e.next.start]=e.next,this.byEnd[e.next.end]=e.next),r)return!0;e=e.previous}while(e);return!1},MagicString.prototype.trimEnd=function(t){return this.trimEndAborted(t),this},MagicString.prototype.trimStartAborted=function(t){var n=new RegExp("^"+(t||"\\s")+"+");if(this.intro=this.intro.replace(n,""),this.intro.length)return!0;var e=this.firstChunk;do{var i=e.end,r=e.trimStart(n);if(e.end!==i&&(e===this.lastChunk&&(this.lastChunk=e.next),this.byEnd[e.end]=e,this.byStart[e.next.start]=e.next,this.byEnd[e.next.end]=e.next),r)return!0;e=e.next}while(e);return!1},MagicString.prototype.trimStart=function(t){return this.trimStartAborted(t),this};var hasOwnProp=Object.prototype.hasOwnProperty,Bundle=function(t){void 0===t&&(t={}),this.intro=t.intro||"",this.separator=void 0!==t.separator?t.separator:"\n",this.sources=[],this.uniqueSources=[],this.uniqueSourceIndexByFilename={}};Bundle.prototype.addSource=function(t){if(t instanceof MagicString)return this.addSource({content:t,filename:t.filename,separator:this.separator});if(!isObject(t)||!t.content)throw new Error("bundle.addSource() takes an object with a `content` property, which should be an instance of MagicString, and an optional `filename`");if(["filename","indentExclusionRanges","separator"].forEach(function(n){hasOwnProp.call(t,n)||(t[n]=t.content[n])}),void 0===t.separator&&(t.separator=this.separator),t.filename)if(hasOwnProp.call(this.uniqueSourceIndexByFilename,t.filename)){var n=this.uniqueSources[this.uniqueSourceIndexByFilename[t.filename]];if(t.content.original!==n.content)throw new Error("Illegal source: same filename ("+t.filename+"), different contents")}else this.uniqueSourceIndexByFilename[t.filename]=this.uniqueSources.length,this.uniqueSources.push({filename:t.filename,content:t.content.original});return this.sources.push(t),this},Bundle.prototype.append=function(t,n){return this.addSource({content:new MagicString(t),separator:n&&n.separator||""}),this},Bundle.prototype.clone=function(){var t=new Bundle({intro:this.intro,separator:this.separator});return this.sources.forEach(function(n){t.addSource({filename:n.filename,content:n.content.clone(),separator:n.separator})}),t},Bundle.prototype.generateDecodedMap=function(t){var n=this;void 0===t&&(t={});var e=[];this.sources.forEach(function(t){Object.keys(t.content.storedNames).forEach(function(t){~e.indexOf(t)||e.push(t)})});var i=new Mappings(t.hires);return this.intro&&i.advance(this.intro),this.sources.forEach(function(t,r){r>0&&i.advance(n.separator);var o=t.filename?n.uniqueSourceIndexByFilename[t.filename]:-1,s=t.content,a=getLocator(s.original);s.intro&&i.advance(s.intro),s.firstChunk.eachNext(function(n){var r=a(n.start);n.intro.length&&i.advance(n.intro),t.filename?n.edited?i.addEdit(o,n.content,r,n.storeName?e.indexOf(n.original):-1):i.addUneditedChunk(o,n,s.original,r,s.sourcemapLocations):i.advance(n.content),n.outro.length&&i.advance(n.outro)}),s.outro&&i.advance(s.outro)}),{file:t.file?t.file.split(/[\/\\]/).pop():null,sources:this.uniqueSources.map(function(n){return t.file?getRelativePath(t.file,n.filename):n.filename}),sourcesContent:this.uniqueSources.map(function(n){return t.includeContent?n.content:null}),names:e,mappings:i.raw}},Bundle.prototype.generateMap=function(t){return new SourceMap(this.generateDecodedMap(t))},Bundle.prototype.getIndentString=function(){var t={};return this.sources.forEach(function(n){var e=n.content.indentStr;null!==e&&(t[e]||(t[e]=0),t[e]+=1)}),Object.keys(t).sort(function(n,e){return t[n]-t[e]})[0]||"\t"},Bundle.prototype.indent=function(t){var n=this;if(arguments.length||(t=this.getIndentString()),""===t)return this;var e=!this.intro||"\n"===this.intro.slice(-1);return this.sources.forEach(function(i,r){var o=void 0!==i.separator?i.separator:n.separator,s=e||r>0&&/\r?\n$/.test(o);i.content.indent(t,{exclude:i.indentExclusionRanges,indentStart:s}),e="\n"===i.content.lastChar()}),this.intro&&(this.intro=t+this.intro.replace(/^[^\n]/gm,function(n,e){return e>0?t+n:n})),this},Bundle.prototype.prepend=function(t){return this.intro=t+this.intro,this},Bundle.prototype.toString=function(){var t=this,n=this.sources.map(function(n,e){var i=void 0!==n.separator?n.separator:t.separator;return(e>0?i:"")+n.content.toString()}).join("");return this.intro+n},Bundle.prototype.isEmpty=function(){return(!this.intro.length||!this.intro.trim())&&!this.sources.some(function(t){return!t.content.isEmpty()})},Bundle.prototype.length=function(){return this.sources.reduce(function(t,n){return t+n.content.length()},this.intro.length)},Bundle.prototype.trimLines=function(){return this.trim("[\\r\\n]")},Bundle.prototype.trim=function(t){return this.trimStart(t).trimEnd(t)},Bundle.prototype.trimStart=function(t){var n=new RegExp("^"+(t||"\\s")+"+");if(this.intro=this.intro.replace(n,""),!this.intro){var e,i=0;do{if(!(e=this.sources[i++]))break}while(!e.content.trimStartAborted(t))}return this},Bundle.prototype.trimEnd=function(t){var n,e=new RegExp((t||"\\s")+"+$"),i=this.sources.length-1;do{if(!(n=this.sources[i--])){this.intro=this.intro.replace(e,"");break}}while(!n.content.trimEndAborted(t));return this};export default MagicString;export{Bundle,SourceMap};
2 |
--------------------------------------------------------------------------------
/demos/README.md:
--------------------------------------------------------------------------------
1 | # shimport-demos
2 |
3 | Demos for [Shimport](https://github.com/Rich-Harris/shimport). https://shimport-demos.surge.sh
--------------------------------------------------------------------------------
/demos/build.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 |
3 | process.chdir(__dirname);
4 |
5 | const html = fs.readFileSync('index.html', 'utf-8');
6 |
7 | const dirs = fs.readdirSync('.').filter(file => {
8 | if (file === 'node_modules') return;
9 | if (file[0] === '.') return;
10 |
11 | return fs.statSync(file).isDirectory();
12 | });
13 |
14 | fs.writeFileSync('index.html', html
15 | .replace(/([\s\S]*)<\/ul>/, `${
16 | dirs.map(dir => `\n\t\t${dir} `).join('')
17 | }\n\t `));
--------------------------------------------------------------------------------
/demos/dynamic-import/dynamic-import.js:
--------------------------------------------------------------------------------
1 | export const message = 'this module was loaded dynamically with import()';
--------------------------------------------------------------------------------
/demos/dynamic-import/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dynamic import
7 |
8 |
9 |
10 | Dynamic import
11 |
12 |
13 | click me
14 | ...
15 |
16 |
17 |
31 |
32 |
--------------------------------------------------------------------------------
/demos/dynamic-import/main.js:
--------------------------------------------------------------------------------
1 | document.querySelector('button').addEventListener('click', () => {
2 | import('./dynamic-import.js').then(({ message }) => {
3 | document.querySelector('p').innerHTML = message;
4 | });
5 | });
--------------------------------------------------------------------------------
/demos/force-shimport/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Force Shimport
7 |
8 |
9 |
10 | Force Shimport
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/demos/force-shimport/main.js:
--------------------------------------------------------------------------------
1 | import * as other from './other.js';
2 |
3 | const main = document.querySelector('main');
4 | main.innerHTML = other.message;
--------------------------------------------------------------------------------
/demos/force-shimport/other.js:
--------------------------------------------------------------------------------
1 | const message = `
2 | Normally, you'd use Shimport with a snippet like the following:
3 |
4 |
5 | var __s = document.createElement('script');
6 | try {
7 | new Function('import("")');
8 | __s.type = 'module';
9 | __s.src = './main.js';
10 | } catch (e) {
11 | __s.src = 'https://unpkg.com/shimport'
12 | __s.dataset.main = './main.js';
13 | }
14 |
15 | document.head.appendChild(__s);
16 |
17 |
18 | With this code, Shimport is only used if the browser doesn't support modules (or dynamic import).
19 |
20 | You might want to always use Shimport, regardless of support — e.g. for testing or performance profiling. In those cases, you can simply add a <script> tag that loads Shimport and tells it where to find your app:
21 |
22 |
23 | <script src="https://unpkg.com/shimport" data-main="./main.js"></script>
24 |
25 | `;
26 |
27 | export { message };
--------------------------------------------------------------------------------
/demos/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Shimport demos
7 |
8 |
9 |
10 | Shimport demos
11 |
12 | Use JavaScript modules in all browsers, today — including dynamic import()
.
13 | Info: github.com/rich-harris/shimport
14 |
15 |
23 |
24 |
--------------------------------------------------------------------------------
/demos/load-from-unpkg/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Load from unpkg.com
7 |
8 |
9 |
10 | Load from unpkg.com
11 |
12 |
13 | loading unpkg.com/lodash-es/range.js ...
14 |
15 |
16 |
30 |
31 |
--------------------------------------------------------------------------------
/demos/load-from-unpkg/main.js:
--------------------------------------------------------------------------------
1 | import range from 'https://unpkg.com/lodash-es/range.js?module';
2 |
3 | document.querySelector('main').innerHTML += `
4 |
5 | range(1, 10)
6 | -> ${range(1, 10).join(', ')}
7 |
8 |
9 | You might have noticed this took a few moments to appear.
10 |
11 | If you look at the network tab in your devtools and reload, you'll see why: range.js
depends on many other modules in the lodash-es
package, which must be downloaded individually.
12 |
13 | This is why, in production, you should use a bundler to combine the individual modules in your app into a few coarse-grained chunks. Rollup can do this for you, and will output native JavaScript modules that can be used with Shimport.
14 | `;
--------------------------------------------------------------------------------
/demos/static-imports/a.js:
--------------------------------------------------------------------------------
1 | export default 'hello from a.js ! this is a default export';
--------------------------------------------------------------------------------
/demos/static-imports/b.js:
--------------------------------------------------------------------------------
1 | export const message = 'hello from b.js ! this is a named export';
--------------------------------------------------------------------------------
/demos/static-imports/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Static imports
7 |
8 |
9 |
10 | Static imports
11 |
12 |
13 |
14 |
28 |
29 |
--------------------------------------------------------------------------------
/demos/static-imports/main.js:
--------------------------------------------------------------------------------
1 | import a from './a.js';
2 | import { message } from './b.js';
3 |
4 | document.body.innerHTML += `
5 | ${a}
6 | ${message}
7 | `;
--------------------------------------------------------------------------------
/demos/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
3 | max-width: 800px;
4 | margin: 0 auto;
5 | padding: 2em 1em;
6 | line-height: 1.5;
7 | }
8 |
9 | button {
10 | font-family: inherit;
11 | font-size: inherit;
12 | padding: 0.5em 1em;
13 | background-color: #333;
14 | color: white;
15 | border: none;
16 | border-radius: 2px;
17 | cursor: pointer;
18 | }
19 |
20 | button:hover {
21 | background-color: #555;
22 | }
23 |
24 | pre {
25 | tab-size: 2;
26 | background-color: #f4f4f4;
27 | padding: 0.5em;
28 | border-radius: 2px;
29 | }
30 |
31 | code {
32 | position: relative;
33 | background-color: #f4f4f4;
34 | padding: 0.3em 0.6em;
35 | top: -0.1em;
36 | line-height: 1;
37 | border-radius: 2px;
38 | }
--------------------------------------------------------------------------------
/demos/transform/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Transform
7 |
8 |
9 |
56 |
57 |
58 | Transform
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
105 |
106 |
--------------------------------------------------------------------------------
/demos/web-worker/app/bar.js:
--------------------------------------------------------------------------------
1 | export const bar = `hello from ${location.origin}/web-worker/app/bar.js!`;
--------------------------------------------------------------------------------
/demos/web-worker/app/foo.js:
--------------------------------------------------------------------------------
1 | export const foo = `hello from ${location.origin}/web-worker/app/foo.js!`;
--------------------------------------------------------------------------------
/demos/web-worker/app/main.js:
--------------------------------------------------------------------------------
1 | import { foo } from './foo.js';
2 |
3 | console.log(foo);
4 |
5 | import('./bar.js').then(({ bar }) => {
6 | console.log(bar);
7 | });
--------------------------------------------------------------------------------
/demos/web-worker/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Web workers
7 |
8 |
9 |
10 | Web workers
11 |
12 | You can use Shimport in web workers via importScripts
. This is the code inside worker.js — you can see the output by opening the console.
13 |
14 | worker.js
15 |
16 | importScripts('../shimport.js');
17 | __shimport__.load('./app/main.js', location.href);
18 |
19 | app/main.js
20 |
21 | import { foo } from './foo.js';
22 |
23 | console.log(foo);
24 |
25 | import('./bar.js').then(({ bar }) => {
26 | console.log(bar);
27 | });
28 |
29 | app/foo.js
30 |
31 | export const foo = `hello from ${location.origin}/web-worker/app/foo.js!`;
32 |
33 | app/bar.js
34 |
35 | export const bar = `hello from ${location.origin}/web-worker/app/bar.js!`;
36 |
37 |
40 |
41 |
--------------------------------------------------------------------------------
/demos/web-worker/worker.js:
--------------------------------------------------------------------------------
1 | importScripts('../shimport.dev.js');
2 |
3 | const main = new URL('./app/main.js', location.href);
4 | __shimport__.load(main.href);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shimport",
3 | "description": "Shim for dynamic import()",
4 | "version": "2.0.5",
5 | "repository": "Rich-Harris/shimport",
6 | "main": "index.js",
7 | "files": [
8 | "index.js",
9 | "index.dev.js"
10 | ],
11 | "devDependencies": {
12 | "@rollup/plugin-replace": "^2.3.3",
13 | "@rollup/plugin-typescript": "^6.0.0",
14 | "@types/mocha": "^8.0.3",
15 | "@types/node": "^14.11.1",
16 | "kleur": "^4.1.1",
17 | "locate-character": "^2.0.5",
18 | "pretty-ms": "^7.0.0",
19 | "rollup": "^2.27.1",
20 | "rollup-plugin-terser": "^7.0.2",
21 | "sirv-cli": "^1.0.6",
22 | "sucrase": "^3.15.0",
23 | "surge": "^0.21.6",
24 | "typescript": "^4.0.3",
25 | "uvu": "^0.3.3"
26 | },
27 | "scripts": {
28 | "dev": "rollup -cw",
29 | "build": "rollup -c",
30 | "test": "uvu -r sucrase/register/ts test test.ts",
31 | "prepublishOnly": "npm test && npm run build",
32 | "deploy": "npm run build && npm run demos:build && surge demos --domain shimport-demos.surge.sh",
33 | "demos:build": "cp index.js demos/shimport.js && cp index.dev.js demos/shimport.dev.js && node demos/build.js",
34 | "demos:start": "sirv demos --dev"
35 | },
36 | "license": "MIT"
37 | }
38 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import typescript from '@rollup/plugin-typescript';
2 | import replace from '@rollup/plugin-replace';
3 | import { terser } from 'rollup-plugin-terser';
4 | import pkg from './package.json';
5 |
6 | const config = dev => ({
7 | input: 'src/index.ts',
8 | output: [
9 | {
10 | file: dev ? 'index.dev.js' : pkg.main,
11 | format: 'iife',
12 | name: '__shimport__'
13 | }
14 | ],
15 | plugins: [
16 | replace({
17 | __VERSION__: pkg.version
18 | }),
19 | typescript({
20 | typescript: require('typescript')
21 | }),
22 | !dev && terser({
23 | output: {
24 | comments: false
25 | }
26 | })
27 | ]
28 | })
29 |
30 | export default [
31 | config(false),
32 | config(true)
33 | ];
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { transform } from './transform';
2 | import { define, load } from './load';
3 |
4 | interface Script extends Element {
5 | dataset: Record;
6 | }
7 |
8 | if (typeof document !== 'undefined') {
9 | const scr: Script = document.querySelector('[data-main]');
10 | if (scr) {
11 | load(new URL(scr.getAttribute('data-main'), document.baseURI));
12 | }
13 | }
14 |
15 | const VERSION = "__VERSION__";
16 |
17 | export { transform, define, load, VERSION };
--------------------------------------------------------------------------------
/src/load.ts:
--------------------------------------------------------------------------------
1 | import { transform } from './transform';
2 |
3 | const promises: Record> = {};
4 |
5 | type __Import = (id: string) => Promise;
6 | type __Exports = Record;
7 |
8 | export function define(
9 | id: string,
10 | deps: string[],
11 | factory: (__import: __Import, __exports: __Exports, ...deps: any[]) => void
12 | ) {
13 | const __import = (dep: string) => load(new URL(dep, id));
14 |
15 | return Promise.all(deps.map(__import)).then(__deps => {
16 | const __exports = {};
17 |
18 | factory(__import, __exports, ...__deps);
19 | return __exports;
20 | });
21 | }
22 |
23 | export function load(url: string | URL) {
24 | return promises[url] || (
25 | promises[url] = fetch(url)
26 | .then(r => r.text())
27 | .then(text => evaluate(transform(text, url)))
28 | );
29 | }
30 |
31 | let uid = 1;
32 |
33 | function evaluate(code: string) {
34 | if (typeof document !== 'undefined' && typeof URL !== 'undefined') {
35 | return new Promise(fulfil => {
36 | const id = `__shimport__${uid++}`;
37 |
38 | // creating a script tag gives us proper stack traces
39 | const blob = new Blob([`${id}=${code}`], {
40 | type: 'application/javascript'
41 | });
42 |
43 | const script = document.createElement('script');
44 | script.src = URL.createObjectURL(blob);
45 |
46 | script.onload = () => {
47 | fulfil((window as any)[id]);
48 | delete (window as any)[id];
49 | };
50 |
51 | document.head.appendChild(script);
52 | });
53 |
54 | } else {
55 | // for browsers without `URL`
56 | return (0,eval)(code);
57 | }
58 | }
--------------------------------------------------------------------------------
/src/transform.ts:
--------------------------------------------------------------------------------
1 | type State = {
2 | name?: string;
3 | pattern: RegExp;
4 | handlers: Array<(i?: number, token?: string) => State | void>
5 | };
6 |
7 | type Specifier = {
8 | name: string;
9 | as: string;
10 | }
11 |
12 | interface Range {
13 | start: number;
14 | end: number;
15 | [key: string]: any;
16 | }
17 |
18 | function get_alias(specifiers: Specifier[], name: string) {
19 | let i = specifiers.length;
20 | while (i--) {
21 | if (specifiers[i].name === name) return specifiers[i].as;
22 | }
23 | }
24 |
25 | function importDecl(str: string, start: number, end: number, specifiers: Specifier[], source: string) {
26 | const name = get_alias(specifiers, '*') || get_alias(specifiers, 'default');
27 |
28 | return {
29 | start,
30 | end,
31 | source,
32 | name,
33 | specifiers,
34 | toString() {
35 | return `/*${str.slice(start, end)}*/`;
36 | }
37 | };
38 | }
39 |
40 | function exportDefaultDeclaration(str: string, start: number, end: number) {
41 | const match = /^\s*(?:(class)(\s+extends|\s*{)|(function)\s*\()/.exec(str.slice(end));
42 |
43 | if (match) {
44 | // anonymous class declaration
45 | end += match[0].length;
46 |
47 | const name = '__default_export';
48 |
49 | return {
50 | start,
51 | end,
52 | name,
53 | as: 'default',
54 | toString() {
55 | return match[1]
56 | ? `class ${name}${match[2]}`
57 | : `function ${name}(`;
58 | }
59 | };
60 | }
61 |
62 | return {
63 | start,
64 | end,
65 | toString() {
66 | return `__exports.default =`;
67 | }
68 | };
69 | }
70 |
71 | function exportSpecifiersDeclaration(str: string, start: number, specifiersStart: number, specifiersEnd: number, end: number, source: string) {
72 | const specifiers = processSpecifiers(str.slice(specifiersStart + 1, specifiersEnd - 1).trim());
73 |
74 | return {
75 | start,
76 | end,
77 | source,
78 | toString(nameBySource: Map) {
79 | const name = source && nameBySource.get(source);
80 |
81 | return specifiers
82 | .map(s => {
83 | return `__exports.${s.as} = ${name ? `${name}.${s.name}` : s.name}; `;
84 | })
85 | .join('') + `/*${str.slice(start, end)}*/`
86 | }
87 | };
88 | }
89 |
90 | function exportDecl(str: string, start: number, c: number) {
91 | const end = c;
92 |
93 | while (str[c] && /\S/.test(str[c])) c += 1;
94 | while (str[c] && !/\S/.test(str[c])) c += 1;
95 |
96 | const nameStart = c;
97 | while (str[c] && !punctuatorChars.test(str[c]) && !isWhitespace(str[c])) c += 1;
98 | const nameEnd = c;
99 |
100 | const name = str.slice(nameStart, nameEnd);
101 |
102 | return {
103 | start,
104 | end,
105 | name,
106 | toString() {
107 | return '';
108 | }
109 | };
110 | }
111 |
112 | function exportStarDeclaration(str: string, start: number, end: number, source: string) {
113 | return {
114 | start,
115 | end,
116 | source,
117 | toString(nameBySource: Map) {
118 | return `Object.assign(__exports, ${nameBySource.get(source)}); /*${str.slice(start, end)}*/`;
119 | }
120 | };
121 | }
122 |
123 | const keywords = /\b(case|default|delete|do|else|in|instanceof|new|return|throw|typeof|void)\s*$/;
124 | const punctuators = /(^|\{|\(|\[\.|;|,|<|>|<=|>=|==|!=|===|!==|\+|-|\*\%|<<|>>|>>>|&|\||\^|!|~|&&|\|\||\?|:|=|\+=|-=|\*=|%=|<<=|>>=|>>>=|&=|\|=|\^=|\/=|\/)\s*$/;
125 | const ambiguous = /(\}|\)|\+\+|--)\s*$/;
126 |
127 | const punctuatorChars = /[{}()[.;,<>=+\-*%&|\^!~?:/]/;
128 | const keywordChars = /[a-zA-Z_$0-9]/;
129 |
130 | const whitespace_obj = { ' ': 1, '\t': 1, '\n': 1, '\r': 1, '\f': 1, '\v': 1, '\u00A0': 1, '\u2028': 1, '\u2029': 1 };
131 |
132 | function isWhitespace(char: string) {
133 | // this is faster than testing a regex
134 | return char in whitespace_obj;
135 | }
136 |
137 | function isQuote(char: string) {
138 | return char === "'" || char === '"';
139 | }
140 |
141 | const namespaceImport = /^\*\s+as\s+(\w+)$/;
142 | const defaultAndStarImport = /(\w+)\s*,\s*\*\s*as\s*(\w+)$/;
143 | const defaultAndNamedImport = /(\w+)\s*,\s*{(.+)}$/;
144 |
145 | function processImportSpecifiers(str: string): Specifier[] {
146 | let match = namespaceImport.exec(str);
147 | if (match) {
148 | return [{ name: '*', as: match[1] }];
149 | }
150 |
151 | match = defaultAndStarImport.exec(str);
152 | if (match) {
153 | return [{ name: 'default', as: match[1] }, { name: '*', as: match[2] }];
154 | }
155 |
156 | match = defaultAndNamedImport.exec(str);
157 | if (match) {
158 | return [{ name: 'default', as: match[1] }].concat(processSpecifiers(match[2].trim()));
159 | }
160 |
161 | if (str[0] === '{') return processSpecifiers(str.slice(1, -1).trim());
162 |
163 | if (str) return [{ name: 'default', as: str }];
164 |
165 | return [];
166 | }
167 |
168 | function processSpecifiers(str: string) {
169 | return str
170 | ? str.split(',').map(part => {
171 | const [name, , as] = part.trim().split(/[^\S]+/);
172 | return { name, as: as || name };
173 | })
174 | : [];
175 | }
176 |
177 | function getImportDeclaration(str: string, i: number) {
178 | const start = i;
179 |
180 | const specifierStart = i += 6;
181 | while (str[i] && isWhitespace(str[i])) i += 1;
182 | while (str[i] && !isQuote(str[i])) i += 1;
183 | const specifierEnd = i;
184 |
185 | const sourceStart = i += 1;
186 | while (str[i] && !isQuote(str[i])) i += 1;
187 | const sourceEnd = i++;
188 |
189 | return importDecl(
190 | str,
191 | start,
192 | i,
193 | processImportSpecifiers(str.slice(specifierStart, specifierEnd).replace(/from\s*$/, '').trim()),
194 | str.slice(sourceStart, sourceEnd)
195 | );
196 | }
197 |
198 | function getImportStatement(i: number) {
199 | return {
200 | start: i,
201 | end: i + 6,
202 | toString() {
203 | return '__import'
204 | }
205 | };
206 | }
207 |
208 | const importMetaUrlPattern = /^import\s*\.\s*meta\s*\.\s*url/;
209 |
210 | function getImportMetaUrl(str: string, start: number, id: string) {
211 | const match = importMetaUrlPattern.exec(str.slice(start));
212 | if (match) {
213 | return {
214 | start,
215 | end: start + match[0].length,
216 | toString() {
217 | return JSON.stringify('' + id);
218 | }
219 | }
220 | }
221 | }
222 |
223 | function getExportDeclaration(str: string, i: number) {
224 | const start = i;
225 |
226 | i += 6;
227 | while (str[i] && isWhitespace(str[i])) i += 1;
228 |
229 | const declarationStart = i;
230 |
231 | if (str[i] === '{') {
232 | while (str[i] !== '}') i += 1;
233 | i += 1;
234 |
235 | const specifiersEnd = i;
236 |
237 | let source = null;
238 |
239 | while (isWhitespace(str[i])) i += 1;
240 | if (/^from[\s\n'"]/.test(str.slice(i, i + 5))) {
241 | i += 4;
242 | while (isWhitespace(str[i])) i += 1;
243 |
244 | while (str[i] && !isQuote(str[i])) i += 1;
245 | const sourceStart = i += 1;
246 | while (str[i] && !isQuote(str[i])) i += 1;
247 |
248 | source = str.slice(sourceStart, i);
249 | i += 1;
250 | }
251 |
252 | return exportSpecifiersDeclaration(
253 | str,
254 | start,
255 | declarationStart,
256 | specifiersEnd,
257 | i,
258 | source
259 | );
260 | }
261 |
262 | if (str[i] === '*') {
263 | i += 1;
264 | while (isWhitespace(str[i])) i += 1;
265 | i += 4;
266 | while (str[i] && !isQuote(str[i])) i += 1;
267 |
268 | const sourceStart = i += 1;
269 | while (str[i] && !isQuote(str[i])) i += 1;
270 | const sourceEnd = i++;
271 |
272 | return exportStarDeclaration(
273 | str,
274 | start,
275 | i,
276 | str.slice(sourceStart, sourceEnd)
277 | );
278 | }
279 |
280 | if (/^default\b/.test(str.slice(i, i + 8))) {
281 | return exportDefaultDeclaration(
282 | str,
283 | start,
284 | declarationStart + 7
285 | );
286 | }
287 |
288 | return exportDecl(
289 | str,
290 | start,
291 | declarationStart
292 | );
293 | }
294 |
295 | function find(str: string, id: string): [Range[], Range[], Range[], Range[]] {
296 | let escapedFrom: State;
297 | let regexEnabled = true;
298 | let pfixOp = false;
299 |
300 | const stack: State[] = [];
301 |
302 | let lsci = -1; // last significant character index
303 | const lsc = () => str[lsci];
304 |
305 | var parenMatches: Record = {};
306 | var openingParenPositions: Record = {};
307 | var parenDepth = 0;
308 |
309 | const importDeclarations: Range[] = [];
310 | const importStatements: Range[] = [];
311 | const importMetaUrls: Range[] = [];
312 | const exportDeclarations: Range[] = [];
313 |
314 | function tokenClosesExpression() {
315 | if (lsc() === ')') {
316 | var c = parenMatches[lsci];
317 | while (isWhitespace(str[c - 1])) {
318 | c -= 1;
319 | }
320 |
321 | // if parenthesized expression is immediately preceded by `if`/`while`, it's not closing an expression
322 | return !/(if|while)$/.test(str.slice(c - 5, c));
323 | }
324 |
325 | // TODO handle }, ++ and -- tokens immediately followed by / character
326 | return true;
327 | }
328 |
329 | const base: State = {
330 | pattern: /(?:(\()|(\))|({)|(})|(")|(')|(\/\/)|(\/\*)|(\/)|(`)|(import)|(export)|(\+\+|--))/g,
331 |
332 | handlers: [
333 | // (
334 | (i: number) => {
335 | lsci = i;
336 | openingParenPositions[parenDepth++] = i;
337 | },
338 |
339 | // )
340 | (i: number) => {
341 | lsci = i;
342 | parenMatches[i] = openingParenPositions[--parenDepth];
343 | },
344 |
345 | // {
346 | (i: number) => {
347 | lsci = i;
348 | stack.push(base);
349 | },
350 |
351 | // }
352 | (i: number) => {
353 | lsci = i;
354 | return stack.pop();
355 | },
356 |
357 | // "
358 | (i: number) => {
359 | stack.push(base);
360 | return double_quoted;
361 | },
362 |
363 | // '
364 | (i: number) => {
365 | stack.push(base);
366 | return single_quoted;
367 | },
368 |
369 | // //
370 | (i: number) => line_comment,
371 |
372 | // /*
373 | (i: number) => block_comment,
374 |
375 | // /
376 | (i: number) => {
377 | // could be start of regex literal OR division punctuator. Solution via
378 | // http://stackoverflow.com/questions/5519596/when-parsing-javascript-what-determines-the-meaning-of-a-slash/27120110#27120110
379 |
380 | var b = i;
381 | while (b > 0 && isWhitespace(str[b - 1])) {
382 | b -= 1;
383 | }
384 |
385 | if (b > 0) {
386 | var a = b;
387 |
388 | if (punctuatorChars.test(str[a - 1])) {
389 | while (a > 0 && punctuatorChars.test(str[a - 1])) {
390 | a -= 1;
391 | }
392 | } else {
393 | while (a > 0 && keywordChars.test(str[a - 1])) {
394 | a -= 1;
395 | }
396 | }
397 |
398 | var token = str.slice(a, b);
399 |
400 | regexEnabled = token
401 | ? keywords.test(token) ||
402 | punctuators.test(token) ||
403 | (ambiguous.test(token) && !tokenClosesExpression())
404 | : false;
405 | } else {
406 | regexEnabled = true;
407 | }
408 |
409 | return slash;
410 | },
411 |
412 | // `
413 | (i: number) => template_string,
414 |
415 | // import
416 | (i: number) => {
417 | if (i === 0 || isWhitespace(str[i - 1]) || punctuatorChars.test(str[i - 1])) {
418 | let j = i + 6;
419 | let char;
420 |
421 | do {
422 | char = str[j++];
423 | } while (isWhitespace(char));
424 |
425 | const hasWhitespace = j > i + 7;
426 |
427 | if (/^['"{*]$/.test(char) || (hasWhitespace && /^[a-zA-Z_$]$/.test(char))) {
428 | const d = getImportDeclaration(str, i);
429 | importDeclarations.push(d);
430 | p = d.end;
431 | }
432 |
433 | else if (char === '(') {
434 | const s = getImportStatement(i);
435 | importStatements.push(s);
436 | p = s.end;
437 | }
438 |
439 | else if (char === '.') {
440 | const u = getImportMetaUrl(str, i, id);
441 | if (u) {
442 | importMetaUrls.push(u);
443 | p = u.end;
444 | }
445 | }
446 | }
447 | },
448 |
449 | // export
450 | (i: number) => {
451 | if (i === 0 || isWhitespace(str[i - 1]) || punctuatorChars.test(str[i - 1])) {
452 | if (/export[\s\n{]/.test(str.slice(i, i + 7))) {
453 | const d = getExportDeclaration(str, i);
454 | exportDeclarations.push(d);
455 | p = d.end;
456 | }
457 | }
458 | },
459 |
460 | // ++/--
461 | (i: number) => {
462 | pfixOp = (!pfixOp && str[i - 1] === '+');
463 | }
464 | ]
465 | };
466 |
467 | const slash: State = {
468 | pattern: /(?:(\[)|(\\)|(.))/g,
469 |
470 | handlers: [
471 | // [
472 | (i: number) => regexEnabled ? regex_character : base,
473 |
474 | // \\
475 | (i: number) => ((escapedFrom = regex), escaped),
476 |
477 | // anything else
478 | (i: number) => regexEnabled && !pfixOp ? regex : base
479 | ]
480 | };
481 |
482 | const regex: State = {
483 | pattern: /(?:(\[)|(\\)|(\/))/g,
484 |
485 | handlers: [
486 | // [
487 | () => regex_character,
488 |
489 | // \\
490 | () => ((escapedFrom = regex), escaped),
491 |
492 | // /
493 | () => base
494 | ]
495 | };
496 |
497 | const regex_character: State = {
498 | pattern: /(?:(\])|(\\))/g,
499 |
500 | handlers: [
501 | // ]
502 | () => regex,
503 |
504 | // \\
505 | () => ((escapedFrom = regex_character), escaped)
506 | ]
507 | };
508 |
509 | const double_quoted: State = {
510 | pattern: /(?:(\\)|("))/g,
511 |
512 | handlers: [
513 | // \\
514 | () => ((escapedFrom = double_quoted), escaped),
515 |
516 | // "
517 | () => stack.pop()
518 | ]
519 | };
520 |
521 | const single_quoted: State = {
522 | pattern: /(?:(\\)|('))/g,
523 |
524 | handlers: [
525 | // \\
526 | () => ((escapedFrom = single_quoted), escaped),
527 |
528 | // '
529 | () => stack.pop()
530 | ]
531 | };
532 |
533 | const escaped: State = {
534 | pattern: /(.)/g,
535 |
536 | handlers: [
537 | () => escapedFrom
538 | ]
539 | };
540 |
541 | const template_string: State = {
542 | pattern: /(?:(\${)|(\\)|(`))/g,
543 |
544 | handlers: [
545 | // ${
546 | () => {
547 | stack.push(template_string);
548 | return base;
549 | },
550 |
551 | // \\
552 | () => ((escapedFrom = template_string), escaped),
553 |
554 | // `
555 | () => base
556 | ]
557 | };
558 |
559 | const line_comment = {
560 | pattern: /((?:\n|$))/g,
561 |
562 | handlers: [
563 | // \n
564 | () => base
565 | ]
566 | };
567 |
568 | const block_comment = {
569 | pattern: /(\*\/)/g,
570 |
571 | handlers: [
572 | // \n
573 | () => base
574 | ]
575 | };
576 |
577 | let state = base;
578 |
579 | let p = 0;
580 |
581 | while (p < str.length) {
582 | state.pattern.lastIndex = p;
583 | const match = state.pattern.exec(str);
584 |
585 | if (!match) {
586 | if (stack.length > 0 || state !== base) {
587 | throw new Error(`Unexpected end of file`);
588 | }
589 |
590 | break;
591 | }
592 |
593 | p = match.index + match[0].length;
594 |
595 | for (let j = 1; j < match.length; j += 1) {
596 | if (match[j]) {
597 | state = state.handlers[j - 1](match.index) || state;
598 | break;
599 | }
600 | }
601 | }
602 |
603 | return [
604 | importDeclarations,
605 | importStatements,
606 | importMetaUrls,
607 | exportDeclarations
608 | ];
609 | }
610 |
611 | export function transform(source: string, id: string) {
612 | const [
613 | importDeclarations,
614 | importStatements,
615 | importMetaUrls,
616 | exportDeclarations
617 | ] = find(source, id);
618 |
619 | const nameBySource = new Map();
620 |
621 | importDeclarations.forEach(d => {
622 | if (nameBySource.has(d.source)) return;
623 | nameBySource.set(d.source, d.name || `__dep_${nameBySource.size}`);
624 | });
625 |
626 | exportDeclarations.forEach(d => {
627 | if (!d.source) return;
628 | if (nameBySource.has(d.source)) return;
629 | nameBySource.set(d.source, d.name || `__dep_${nameBySource.size}`);
630 | });
631 |
632 | const deps = Array.from(nameBySource.keys())
633 | .map(s => `'${s}'`)
634 | .join(', ');
635 |
636 | const names = ['__import', '__exports'].concat(Array.from(nameBySource.values()))
637 | .join(', ');
638 |
639 | const hoisted: string[] = [];
640 | importDeclarations.forEach(decl => {
641 | const name = nameBySource.get(decl.source);
642 |
643 | decl.specifiers
644 | .sort((a: Specifier, b: Specifier) => {
645 | if (a.name === 'default') return 1;
646 | if (b.name === 'default') return -1;
647 | })
648 | .forEach((s: Specifier) => {
649 | if (s.name !== '*') {
650 | const assignment = (s.name === 'default' && s.as === name)
651 | ? `${s.as} = ${name}.default; `
652 | : `var ${s.as} = ${name}.${s.name}; `;
653 |
654 | hoisted.push(assignment);
655 | }
656 | });
657 | });
658 |
659 | let transformed = `__shimport__.define('${id}', [${deps}], function(${names}){ ${hoisted.join('')}`;
660 |
661 | const ranges: any[] = [
662 | ...importDeclarations,
663 | ...importStatements,
664 | ...importMetaUrls,
665 | ...exportDeclarations
666 | ].sort((a, b) => a.start - b.start);
667 |
668 | let c = 0;
669 |
670 | for (let i = 0; i < ranges.length; i += 1) {
671 | const range = ranges[i];
672 | transformed += (
673 | source.slice(c, range.start) +
674 | range.toString(nameBySource)
675 | );
676 |
677 | c = range.end;
678 | }
679 |
680 | transformed += source.slice(c);
681 |
682 | exportDeclarations.forEach(d => {
683 | if (d.name) transformed += `\n__exports.${d.as || d.name} = ${d.name};`;
684 | });
685 |
686 | transformed += `\n});\n//# sourceURL=${id}`;
687 |
688 | return transformed;
689 | }
690 |
--------------------------------------------------------------------------------
/test/samples/dynamic-import-minified/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./dynamic-import-minified/input.js', [], function(__import, __exports){ showFoo=()=>__import('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
4 | });
5 | //# sourceURL=./dynamic-import-minified/input.js
--------------------------------------------------------------------------------
/test/samples/dynamic-import-minified/input.js:
--------------------------------------------------------------------------------
1 | showFoo=()=>import('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
--------------------------------------------------------------------------------
/test/samples/dynamic-import-minified/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./dynamic-import-minified/input.js', [], function(__import, __exports){ showFoo=()=>__import('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
4 | });
5 | //# sourceURL=./dynamic-import-minified/input.js
--------------------------------------------------------------------------------
/test/samples/dynamic-import-whitespace/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./dynamic-import-whitespace/input.js', [], function(__import, __exports){ __import ('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
4 | });
5 | //# sourceURL=./dynamic-import-whitespace/input.js
--------------------------------------------------------------------------------
/test/samples/dynamic-import-whitespace/input.js:
--------------------------------------------------------------------------------
1 | import ('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
--------------------------------------------------------------------------------
/test/samples/dynamic-import-whitespace/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./dynamic-import-whitespace/input.js', [], function(__import, __exports){ __import ('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
4 | });
5 | //# sourceURL=./dynamic-import-whitespace/input.js
--------------------------------------------------------------------------------
/test/samples/dynamic-import/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./dynamic-import/input.js', [], function(__import, __exports){ __import('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
4 | });
5 | //# sourceURL=./dynamic-import/input.js
--------------------------------------------------------------------------------
/test/samples/dynamic-import/input.js:
--------------------------------------------------------------------------------
1 | import('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
--------------------------------------------------------------------------------
/test/samples/dynamic-import/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./dynamic-import/input.js', [], function(__import, __exports){ __import('./foo.js').then(foo => {
2 | console.log(foo);
3 | });
4 | });
5 | //# sourceURL=./dynamic-import/input.js
--------------------------------------------------------------------------------
/test/samples/export-declaration/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-declaration/input.js', [], function(__import, __exports){ const foo = 42;
2 | __exports.foo = foo;
3 | });
4 | //# sourceURL=./export-declaration/input.js
--------------------------------------------------------------------------------
/test/samples/export-declaration/input.js:
--------------------------------------------------------------------------------
1 | export const foo = 42;
--------------------------------------------------------------------------------
/test/samples/export-declaration/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-declaration/input.js', [], function(__import, __exports){ const foo = 42;
2 | __exports.foo = foo;
3 | });
4 | //# sourceURL=./export-declaration/input.js
--------------------------------------------------------------------------------
/test/samples/export-default-no-space/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default-no-space/input.js', [], function(__import, __exports){ __exports.default =[42];
2 | });
3 | //# sourceURL=./export-default-no-space/input.js
--------------------------------------------------------------------------------
/test/samples/export-default-no-space/input.js:
--------------------------------------------------------------------------------
1 | export default[42];
--------------------------------------------------------------------------------
/test/samples/export-default-no-space/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default-no-space/input.js', [], function(__import, __exports){ __exports.default =[42];
2 | });
3 | //# sourceURL=./export-default-no-space/input.js
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-class-extends/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default-unnamed-class-extends/input.js', [], function(__import, __exports){ class __default_export extends bar{}foo=42
2 | __exports.default = __default_export;
3 | });
4 | //# sourceURL=./export-default-unnamed-class-extends/input.js
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-class-extends/input.js:
--------------------------------------------------------------------------------
1 | export default class extends bar{}foo=42
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-class-extends/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default-unnamed-class-extends/input.js', [], function(__import, __exports){ class __default_export extends bar{}foo=42
2 | __exports.default = __default_export;
3 | });
4 | //# sourceURL=./export-default-unnamed-class-extends/input.js
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-class/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default-unnamed-class/input.js', [], function(__import, __exports){ class __default_export{}foo=42
2 | __exports.default = __default_export;
3 | });
4 | //# sourceURL=./export-default-unnamed-class/input.js
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-class/input.js:
--------------------------------------------------------------------------------
1 | export default class{}foo=42
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-class/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default-unnamed-class/input.js', [], function(__import, __exports){ class __default_export{}foo=42
2 | __exports.default = __default_export;
3 | });
4 | //# sourceURL=./export-default-unnamed-class/input.js
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-function/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default-unnamed-function/input.js', [], function(__import, __exports){ function __default_export(){}foo=42
2 | __exports.default = __default_export;
3 | });
4 | //# sourceURL=./export-default-unnamed-function/input.js
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-function/input.js:
--------------------------------------------------------------------------------
1 | export default function(){}foo=42
--------------------------------------------------------------------------------
/test/samples/export-default-unnamed-function/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default-unnamed-function/input.js', [], function(__import, __exports){ function __default_export(){}foo=42
2 | __exports.default = __default_export;
3 | });
4 | //# sourceURL=./export-default-unnamed-function/input.js
--------------------------------------------------------------------------------
/test/samples/export-default/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default/input.js', [], function(__import, __exports){ __exports.default = 42;
2 | });
3 | //# sourceURL=./export-default/input.js
--------------------------------------------------------------------------------
/test/samples/export-default/input.js:
--------------------------------------------------------------------------------
1 | export default 42;
--------------------------------------------------------------------------------
/test/samples/export-default/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-default/input.js', [], function(__import, __exports){ __exports.default = 42;
2 | });
3 | //# sourceURL=./export-default/input.js
--------------------------------------------------------------------------------
/test/samples/export-empty/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-empty/input.js', [], function(__import, __exports){ // unambiguous grammar
2 | /*export {}*/;
3 | });
4 | //# sourceURL=./export-empty/input.js
--------------------------------------------------------------------------------
/test/samples/export-empty/input.js:
--------------------------------------------------------------------------------
1 | // unambiguous grammar
2 | export {};
--------------------------------------------------------------------------------
/test/samples/export-empty/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-empty/input.js', [], function(__import, __exports){ // unambiguous grammar
2 | /*export {}*/;
3 | });
4 | //# sourceURL=./export-empty/input.js
--------------------------------------------------------------------------------
/test/samples/export-from-minified/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-from-minified/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ __exports.foo = __dep_0.foo; /*export{foo}from'./foo.js'*/;
2 | });
3 | //# sourceURL=./export-from-minified/input.js
--------------------------------------------------------------------------------
/test/samples/export-from-minified/input.js:
--------------------------------------------------------------------------------
1 | export{foo}from'./foo.js';
--------------------------------------------------------------------------------
/test/samples/export-from-minified/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-from-minified/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ __exports.foo = __dep_0.foo; /*export{foo}from'./foo.js'*/;
2 | });
3 | //# sourceURL=./export-from-minified/input.js
--------------------------------------------------------------------------------
/test/samples/export-from/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-from/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ __exports.foo = __dep_0.foo; /*export { foo } from './foo.js'*/;
2 | });
3 | //# sourceURL=./export-from/input.js
--------------------------------------------------------------------------------
/test/samples/export-from/input.js:
--------------------------------------------------------------------------------
1 | export { foo } from './foo.js';
--------------------------------------------------------------------------------
/test/samples/export-from/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-from/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ __exports.foo = __dep_0.foo; /*export { foo } from './foo.js'*/;
2 | });
3 | //# sourceURL=./export-from/input.js
--------------------------------------------------------------------------------
/test/samples/export-function/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-function/input.js', [], function(__import, __exports){ function foo(a) {}
2 | __exports.foo = foo;
3 | });
4 | //# sourceURL=./export-function/input.js
--------------------------------------------------------------------------------
/test/samples/export-function/input.js:
--------------------------------------------------------------------------------
1 | export function foo(a) {}
--------------------------------------------------------------------------------
/test/samples/export-function/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-function/input.js', [], function(__import, __exports){ function foo(a) {}
2 | __exports.foo = foo;
3 | });
4 | //# sourceURL=./export-function/input.js
--------------------------------------------------------------------------------
/test/samples/export-named-minified-b/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-named-minified-b/input.js', [], function(__import, __exports){ const foo = 42;
2 | const bar = 43;
3 | const baz = 44;
4 | __exports.foo = foo; /*export{foo}*/;__exports.bar = bar; /*export{bar}*/;if(x){}__exports.baz = baz; /*export{baz}*/
5 | });
6 | //# sourceURL=./export-named-minified-b/input.js
--------------------------------------------------------------------------------
/test/samples/export-named-minified-b/input.js:
--------------------------------------------------------------------------------
1 | const foo = 42;
2 | const bar = 43;
3 | const baz = 44;
4 | export{foo};export{bar};if(x){}export{baz}
--------------------------------------------------------------------------------
/test/samples/export-named-minified-b/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-named-minified-b/input.js', [], function(__import, __exports){ const foo = 42;
2 | const bar = 43;
3 | const baz = 44;
4 | __exports.foo = foo; /*export{foo}*/;__exports.bar = bar; /*export{bar}*/;if(x){}__exports.baz = baz; /*export{baz}*/
5 | });
6 | //# sourceURL=./export-named-minified-b/input.js
--------------------------------------------------------------------------------
/test/samples/export-named-minified/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-named-minified/input.js', [], function(__import, __exports){ const foo = 42;
2 | __exports.foo = foo; /*export{foo}*/;
3 | });
4 | //# sourceURL=./export-named-minified/input.js
--------------------------------------------------------------------------------
/test/samples/export-named-minified/input.js:
--------------------------------------------------------------------------------
1 | const foo = 42;
2 | export{foo};
--------------------------------------------------------------------------------
/test/samples/export-named-minified/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-named-minified/input.js', [], function(__import, __exports){ const foo = 42;
2 | __exports.foo = foo; /*export{foo}*/;
3 | });
4 | //# sourceURL=./export-named-minified/input.js
--------------------------------------------------------------------------------
/test/samples/export-named/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-named/input.js', [], function(__import, __exports){ const foo = 42;
2 | __exports.foo = foo; /*export { foo }*/;
3 | });
4 | //# sourceURL=./export-named/input.js
--------------------------------------------------------------------------------
/test/samples/export-named/input.js:
--------------------------------------------------------------------------------
1 | const foo = 42;
2 | export { foo };
--------------------------------------------------------------------------------
/test/samples/export-named/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-named/input.js', [], function(__import, __exports){ const foo = 42;
2 | __exports.foo = foo; /*export { foo }*/;
3 | });
4 | //# sourceURL=./export-named/input.js
--------------------------------------------------------------------------------
/test/samples/export-star-from/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-star-from/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ Object.assign(__exports, __dep_0); /*export * from './foo.js'*/;
2 | });
3 | //# sourceURL=./export-star-from/input.js
--------------------------------------------------------------------------------
/test/samples/export-star-from/input.js:
--------------------------------------------------------------------------------
1 | export * from './foo.js';
--------------------------------------------------------------------------------
/test/samples/export-star-from/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./export-star-from/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ Object.assign(__exports, __dep_0); /*export * from './foo.js'*/;
2 | });
3 | //# sourceURL=./export-star-from/input.js
--------------------------------------------------------------------------------
/test/samples/import-default-star/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-default-star/input.js', ['./x.js'], function(__import, __exports, bar){ var foo = bar.default; /*import foo, * as bar from './x.js'*/;
2 |
3 | console.log(foo, bar);
4 | });
5 | //# sourceURL=./import-default-star/input.js
--------------------------------------------------------------------------------
/test/samples/import-default-star/input.js:
--------------------------------------------------------------------------------
1 | import foo, * as bar from './x.js';
2 |
3 | console.log(foo, bar);
--------------------------------------------------------------------------------
/test/samples/import-default-star/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-default-star/input.js', ['./x.js'], function(__import, __exports, bar){ var foo = bar.default; /*import foo, * as bar from './x.js'*/;
2 |
3 | console.log(foo, bar);
4 | });
5 | //# sourceURL=./import-default-star/input.js
--------------------------------------------------------------------------------
/test/samples/import-default/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-default/input.js', ['./foo.js'], function(__import, __exports, foo){ foo = foo.default; /*import foo from './foo.js'*/;
2 |
3 | console.log(foo);
4 | });
5 | //# sourceURL=./import-default/input.js
--------------------------------------------------------------------------------
/test/samples/import-default/input.js:
--------------------------------------------------------------------------------
1 | import foo from './foo.js';
2 |
3 | console.log(foo);
--------------------------------------------------------------------------------
/test/samples/import-default/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-default/input.js', ['./foo.js'], function(__import, __exports, foo){ foo = foo.default; /*import foo from './foo.js'*/;
2 |
3 | console.log(foo);
4 | });
5 | //# sourceURL=./import-default/input.js
--------------------------------------------------------------------------------
/test/samples/import-empty-minified/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-empty-minified/input.js', ['./foo-polyfill.js'], function(__import, __exports, __dep_0){ /*import'./foo-polyfill.js'*/;
2 |
3 | console.log(window.foo);
4 | });
5 | //# sourceURL=./import-empty-minified/input.js
--------------------------------------------------------------------------------
/test/samples/import-empty-minified/input.js:
--------------------------------------------------------------------------------
1 | import'./foo-polyfill.js';
2 |
3 | console.log(window.foo);
--------------------------------------------------------------------------------
/test/samples/import-empty-minified/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-empty-minified/input.js', ['./foo-polyfill.js'], function(__import, __exports, __dep_0){ /*import'./foo-polyfill.js'*/;
2 |
3 | console.log(window.foo);
4 | });
5 | //# sourceURL=./import-empty-minified/input.js
--------------------------------------------------------------------------------
/test/samples/import-empty/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-empty/input.js', ['./foo-polyfill.js'], function(__import, __exports, __dep_0){ /*import './foo-polyfill.js'*/;
2 |
3 | console.log(window.foo);
4 | });
5 | //# sourceURL=./import-empty/input.js
--------------------------------------------------------------------------------
/test/samples/import-empty/input.js:
--------------------------------------------------------------------------------
1 | import './foo-polyfill.js';
2 |
3 | console.log(window.foo);
--------------------------------------------------------------------------------
/test/samples/import-empty/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-empty/input.js', ['./foo-polyfill.js'], function(__import, __exports, __dep_0){ /*import './foo-polyfill.js'*/;
2 |
3 | console.log(window.foo);
4 | });
5 | //# sourceURL=./import-empty/input.js
--------------------------------------------------------------------------------
/test/samples/import-meta-url/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-meta-url/input.js', [], function(__import, __exports){ console.log("./import-meta-url/input.js");
2 | });
3 | //# sourceURL=./import-meta-url/input.js
--------------------------------------------------------------------------------
/test/samples/import-meta-url/input.js:
--------------------------------------------------------------------------------
1 | console.log(import.meta.url);
--------------------------------------------------------------------------------
/test/samples/import-meta-url/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-meta-url/input.js', [], function(__import, __exports){ console.log("./import-meta-url/input.js");
2 | });
3 | //# sourceURL=./import-meta-url/input.js
--------------------------------------------------------------------------------
/test/samples/import-named-minified-b/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-named-minified-b/input.js', ['./foo.js', './bar.js'], function(__import, __exports, __dep_0, __dep_1){ var foo = __dep_0.foo; var bar = __dep_1.bar; /*import{foo}from './foo.js'*/;/*import{bar}from './bar.js'*/;
2 |
3 | console.log(foo, bar);
4 | });
5 | //# sourceURL=./import-named-minified-b/input.js
--------------------------------------------------------------------------------
/test/samples/import-named-minified-b/input.js:
--------------------------------------------------------------------------------
1 | import{foo}from './foo.js';import{bar}from './bar.js';
2 |
3 | console.log(foo, bar);
--------------------------------------------------------------------------------
/test/samples/import-named-minified-b/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-named-minified-b/input.js', ['./foo.js', './bar.js'], function(__import, __exports, __dep_0, __dep_1){ var foo = __dep_0.foo; var bar = __dep_1.bar; /*import{foo}from './foo.js'*/;/*import{bar}from './bar.js'*/;
2 |
3 | console.log(foo, bar);
4 | });
5 | //# sourceURL=./import-named-minified-b/input.js
--------------------------------------------------------------------------------
/test/samples/import-named-minified/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-named-minified/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ var foo = __dep_0.foo; /*import{foo}from './foo.js'*/;
2 |
3 | console.log(foo);
4 | });
5 | //# sourceURL=./import-named-minified/input.js
--------------------------------------------------------------------------------
/test/samples/import-named-minified/input.js:
--------------------------------------------------------------------------------
1 | import{foo}from './foo.js';
2 |
3 | console.log(foo);
--------------------------------------------------------------------------------
/test/samples/import-named-minified/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-named-minified/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ var foo = __dep_0.foo; /*import{foo}from './foo.js'*/;
2 |
3 | console.log(foo);
4 | });
5 | //# sourceURL=./import-named-minified/input.js
--------------------------------------------------------------------------------
/test/samples/import-named-multiple/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-named-multiple/input.js', ['./x.js'], function(__import, __exports, __dep_0){ var foo = __dep_0.foo; var bar = __dep_0.bar; /*import { foo, bar } from './x.js'*/;
2 |
3 | console.log(foo, bar);
4 | });
5 | //# sourceURL=./import-named-multiple/input.js
--------------------------------------------------------------------------------
/test/samples/import-named-multiple/input.js:
--------------------------------------------------------------------------------
1 | import { foo, bar } from './x.js';
2 |
3 | console.log(foo, bar);
--------------------------------------------------------------------------------
/test/samples/import-named-multiple/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-named-multiple/input.js', ['./x.js'], function(__import, __exports, __dep_0){ var foo = __dep_0.foo; var bar = __dep_0.bar; /*import { foo, bar } from './x.js'*/;
2 |
3 | console.log(foo, bar);
4 | });
5 | //# sourceURL=./import-named-multiple/input.js
--------------------------------------------------------------------------------
/test/samples/import-named/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-named/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ var foo = __dep_0.foo; console.log(foo);
2 | /*import { foo } from './foo.js'*/;
3 | });
4 | //# sourceURL=./import-named/input.js
--------------------------------------------------------------------------------
/test/samples/import-named/input.js:
--------------------------------------------------------------------------------
1 | console.log(foo);
2 | import { foo } from './foo.js';
--------------------------------------------------------------------------------
/test/samples/import-named/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-named/input.js', ['./foo.js'], function(__import, __exports, __dep_0){ var foo = __dep_0.foo; console.log(foo);
2 | /*import { foo } from './foo.js'*/;
3 | });
4 | //# sourceURL=./import-named/input.js
--------------------------------------------------------------------------------
/test/samples/import-namespace/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-namespace/input.js', ['./foo.js'], function(__import, __exports, foo){ /*import * as foo from './foo.js'*/;
2 |
3 | console.log(foo);
4 | });
5 | //# sourceURL=./import-namespace/input.js
--------------------------------------------------------------------------------
/test/samples/import-namespace/input.js:
--------------------------------------------------------------------------------
1 | import * as foo from './foo.js';
2 |
3 | console.log(foo);
--------------------------------------------------------------------------------
/test/samples/import-namespace/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./import-namespace/input.js', ['./foo.js'], function(__import, __exports, foo){ /*import * as foo from './foo.js'*/;
2 |
3 | console.log(foo);
4 | });
5 | //# sourceURL=./import-namespace/input.js
--------------------------------------------------------------------------------
/test/samples/keyword-false-positive/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./keyword-false-positive/input.js', [], function(__import, __exports){ a * Min / x;
2 | });
3 | //# sourceURL=./keyword-false-positive/input.js
--------------------------------------------------------------------------------
/test/samples/keyword-false-positive/input.js:
--------------------------------------------------------------------------------
1 | a * Min / x;
--------------------------------------------------------------------------------
/test/samples/keyword-false-positive/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./keyword-false-positive/input.js', [], function(__import, __exports){ a * Min / x;
2 | });
3 | //# sourceURL=./keyword-false-positive/input.js
--------------------------------------------------------------------------------
/test/samples/template-string-dollar/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./template-string-dollar/input.js', [], function(__import, __exports){ let foo = `$`;
2 | });
3 | //# sourceURL=./template-string-dollar/input.js
--------------------------------------------------------------------------------
/test/samples/template-string-dollar/input.js:
--------------------------------------------------------------------------------
1 | let foo = `$`;
--------------------------------------------------------------------------------
/test/samples/template-string-dollar/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./template-string-dollar/input.js', [], function(__import, __exports){ let foo = `$`;
2 | });
3 | //# sourceURL=./template-string-dollar/input.js
--------------------------------------------------------------------------------
/test/samples/template-string-expression/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./template-string-expression/input.js', [], function(__import, __exports){ let foo = `${42}`;
2 | });
3 | //# sourceURL=./template-string-expression/input.js
--------------------------------------------------------------------------------
/test/samples/template-string-expression/input.js:
--------------------------------------------------------------------------------
1 | let foo = `${42}`;
--------------------------------------------------------------------------------
/test/samples/template-string-expression/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./template-string-expression/input.js', [], function(__import, __exports){ let foo = `${42}`;
2 | });
3 | //# sourceURL=./template-string-expression/input.js
--------------------------------------------------------------------------------
/test/samples/unfinished/actual.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./unfinished/input.js', [], function(__import, __exports){ import /*wut*/
2 | });
3 | //# sourceURL=./unfinished/input.js
--------------------------------------------------------------------------------
/test/samples/unfinished/input.js:
--------------------------------------------------------------------------------
1 | import /*wut*/
--------------------------------------------------------------------------------
/test/samples/unfinished/output.js:
--------------------------------------------------------------------------------
1 | __shimport__.define('./unfinished/input.js', [], function(__import, __exports){ import /*wut*/
2 | });
3 | //# sourceURL=./unfinished/input.js
--------------------------------------------------------------------------------
/test/test.ts:
--------------------------------------------------------------------------------
1 | import { test } from 'uvu';
2 | import * as fs from 'fs';
3 | import * as assert from 'assert';
4 | import * as shimport from '../src/index';
5 |
6 | fs.readdirSync('test/samples').forEach(dir => {
7 | if (dir[0] === '.') return;
8 |
9 | test(dir, () => {
10 | const input = fs.readFileSync(`test/samples/${dir}/input.js`, 'utf-8');
11 | const actual = shimport.transform(input, `./${dir}/input.js`);
12 | fs.writeFileSync(`test/samples/${dir}/actual.js`, actual);
13 |
14 | const expected = fs.readFileSync(`test/samples/${dir}/output.js`, 'utf-8');
15 |
16 | assert.equal(actual, expected);
17 | });
18 | });
19 |
20 | test.run();
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noImplicitAny": true,
4 | "diagnostics": true,
5 | "noImplicitThis": true,
6 | "noEmitOnError": true,
7 | "lib": ["es5", "es6", "dom"]
8 | },
9 | "target": "ES5",
10 | "module": "ES6",
11 | "include": [
12 | "src"
13 | ],
14 | "exclude": [
15 | "node_modules"
16 | ]
17 | }
--------------------------------------------------------------------------------