├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── bench ├── acorn-parse-bench.js ├── esmod-parse-bench.js ├── esmod-transform-bench.js ├── fixture │ ├── react.license.txt │ ├── react@15.4.2.js │ └── rollup.es.js └── index.js ├── index.js ├── package.json └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | 6 | [*.json] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | [*.js] 11 | indent_style = tab 12 | tab_width = 4 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | .DS_Store 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ingvar Stepanyan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esmod 2 | 3 | **UPD**: This was an experiment for a very fast in-place transpilation of import/export. It now is part of https://github.com/standard-things/esm. 4 | -------------------------------------------------------------------------------- /bench/acorn-parse-bench.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { parse } = require('acorn'); 4 | 5 | module.exports = (content) => parse(content, { 6 | sourceType: 'module' 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /bench/esmod-parse-bench.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('../index.js').parse; 4 | -------------------------------------------------------------------------------- /bench/esmod-transform-bench.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('../index.js').transform; 4 | -------------------------------------------------------------------------------- /bench/fixture/react.license.txt: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For React software 4 | 5 | Copyright (c) 2013-present, Facebook, Inc. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | * Neither the name Facebook nor the names of its contributors may be used to 19 | endorse or promote products derived from this software without specific 20 | prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 26 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 29 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /bench/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | 4 | const glob = require('glob') 5 | const { Suite } = require('benchmark') 6 | 7 | const Benchmark = require('benchmark') 8 | const fs = require('fs') 9 | const path = require('path') 10 | const prettyBytes = require('pretty-bytes') 11 | const prettyMs = require('pretty-ms') 12 | 13 | process.chdir(__dirname) 14 | 15 | glob.sync('fixture/*.js').forEach(fixture => { 16 | const content = fs.readFileSync(fixture, 'utf8') 17 | const size = prettyBytes(Buffer.byteLength(content, 'utf8')) 18 | 19 | console.log(`${ fixture } (${ size }):`) 20 | 21 | const suite = new Suite() 22 | glob.sync('./*-bench.js').forEach((id) => { 23 | const fn = require(id); 24 | suite.add(id, () => { 25 | fn(content); 26 | }); 27 | }) 28 | suite.on('error', ({ target: { error } }) => { 29 | throw error 30 | }) 31 | suite.on('cycle', ({ target }) => { 32 | const time = prettyMs(1e3 / target.hz) 33 | console.log(` ${ target }`.replace(' x ', ` in ${ time }; `)) 34 | }) 35 | suite.run() 36 | }) 37 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var acorn = require('acorn'); 4 | var Parser = acorn.Parser; 5 | var pp = Parser.prototype; 6 | 7 | function CustomParser(input) { 8 | Parser.call(this, { 9 | sourceType: 'module' 10 | }, input); 11 | } 12 | 13 | var cp = CustomParser.prototype = Object.create(pp); 14 | 15 | cp.constructor = CustomParser; 16 | 17 | // Kinda like JS engine pre-parsing: 18 | // tokenizes inner block statements, 19 | // including function bodies 20 | // but doesn't create the AST 21 | cp.parseBlock = function () { 22 | var node = this.startNode(); 23 | var length = this.context.length; 24 | do { 25 | this.next(); 26 | } while (this.context.length >= length); 27 | this.next(); // this.expect(tt.braceR); 28 | node.body = []; 29 | return this.finishNode(node, "BlockStatement"); 30 | }; 31 | 32 | cp.parseImport = function () { 33 | var node = pp.parseImport.apply(this, arguments); 34 | node.transformed = ''; 35 | return node; 36 | }; 37 | 38 | cp.parseExport = function () { 39 | var node = pp.parseExport.apply(this, arguments); 40 | node.transformed = ''; 41 | return node; 42 | }; 43 | 44 | function parse(input) { 45 | return new CustomParser(input).parse(); 46 | } 47 | 48 | function generate(input, ast) { 49 | var output = ''; 50 | // Keep note of where we stopped 51 | var lastPos = 0; 52 | // Going only over top-level nodes 53 | for (var i = 0; i < ast.body.length; i++) { 54 | var node = ast.body[i]; 55 | var raw = node.transformed; 56 | // Skipping any nodes that weren't transformed 57 | if (raw !== undefined) { 58 | // Insert anything since the last node 59 | // (comments and other code) 60 | output += input.slice(lastPos, node.start); 61 | // Insert the transformed node and mark new pos 62 | output += raw; 63 | lastPos = node.end; 64 | } 65 | } 66 | // Insert rest of the code after the last transformed node 67 | output += input.slice(lastPos); 68 | return output; 69 | }; 70 | 71 | function transform(input) { 72 | return generate(input, parse(input)); 73 | } 74 | 75 | module.exports = { 76 | parse: parse, 77 | generate: generate, 78 | transform: transform 79 | }; 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esmod", 3 | "version": "1.0.0", 4 | "description": "Fast in-place import/export transpiler", 5 | "main": "index.js", 6 | "scripts": { 7 | "bench": "node bench", 8 | "test": "node test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/rreverser/esmod.git" 13 | }, 14 | "keywords": [ 15 | "es6", 16 | "module", 17 | "import", 18 | "export", 19 | "transpiler", 20 | "fast", 21 | "ast" 22 | ], 23 | "author": "Ingvar Stepanyan (https://rreverser.com/)", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/rreverser/esmod/issues" 27 | }, 28 | "homepage": "https://github.com/rreverser/esmod#readme", 29 | "dependencies": { 30 | "acorn": "^4.0.11" 31 | }, 32 | "devDependencies": { 33 | "benchmark": "^2.1.3", 34 | "glob": "^7.1.1", 35 | "pretty-bytes": "^4.0.2", 36 | "pretty-ms": "^2.1.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { parse, generate, transform } = require('./'); 4 | const assert = require('assert'); 5 | 6 | var source = ` 7 | import 'smth'; 8 | while (1) { 9 | /* looks like block, but it's not: { 10 | } */ 11 | smth 12 | } 13 | export const answer = 42; 14 | `; 15 | 16 | var tests = { 17 | 'parsing does not include BlockStatement body'() { 18 | assert.deepEqual(parse(source).body[1].body, { 19 | type: 'BlockStatement', 20 | body: [], 21 | start: 28, 22 | end: 85 23 | }); 24 | }, 25 | 26 | 'generate respects transformations and formatting'() { 27 | var code = '/*a*/123//b'; 28 | var ast = parse(code); 29 | assert.equal(generate(code, ast), '/*a*/123//b'); 30 | ast.body[0].transformed = ''; 31 | assert.equal(generate(code, ast), '/*a*///b'); 32 | }, 33 | 34 | 'transform can do round-trip changing only import/export'() { 35 | assert.equal(transform(source), ` 36 | 37 | while (1) { 38 | /* looks like block, but it's not: { 39 | } */ 40 | smth 41 | } 42 | 43 | `.replace(/^\t\t/gm, '')); 44 | } 45 | }; 46 | 47 | for (var key in tests) { 48 | try { 49 | tests[key](); 50 | console.info('[PASS] ' + key); 51 | } catch (err) { 52 | console.error('[FAIL] ' + key + '\n' + err.stack); 53 | } 54 | } 55 | --------------------------------------------------------------------------------