From b5c5e9db5fd7127db49b80750f90e7bc932c5028 Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Sat, 24 Oct 2020 11:40:17 +0200 Subject: [PATCH 1/4] WIP cjs transform with cjs module lexer --- demo/public/foo.js | 1 + src/lib/cjs-module-lexer.js | 1865 +++++++++++++++++++++++++++++++++ src/lib/transform-requires.js | 53 + 3 files changed, 1919 insertions(+) create mode 100644 demo/public/foo.js create mode 100644 src/lib/cjs-module-lexer.js create mode 100644 src/lib/transform-requires.js diff --git a/demo/public/foo.js b/demo/public/foo.js new file mode 100644 index 000000000..fc4b1cbcf --- /dev/null +++ b/demo/public/foo.js @@ -0,0 +1 @@ +export const baz = 'yikes'; diff --git a/src/lib/cjs-module-lexer.js b/src/lib/cjs-module-lexer.js new file mode 100644 index 000000000..65271a15c --- /dev/null +++ b/src/lib/cjs-module-lexer.js @@ -0,0 +1,1865 @@ +/* eslint-disable */ +let source, pos, end; +let openTokenDepth, + templateDepth, + lastTokenPos, + lastSlashWasDivision, + templateStack, + templateStackDepth, + openTokenPosStack, + openClassPosStack, + nextBraceIsClass, + starExportMap, + lastStarExportSpecifier, + _exports, + reexports, + requires; + +function resetState() { + openTokenDepth = 0; + templateDepth = -1; + lastTokenPos = -1; + lastSlashWasDivision = false; + templateStack = new Array(1024); + templateStackDepth = 0; + openTokenPosStack = new Array(1024); + openClassPosStack = new Array(1024); + nextBraceIsClass = false; + starExportMap = Object.create(null); + lastStarExportSpecifier = null; + + _exports = new Set(); + reexports = new Set(); + requires = []; +} + +const strictReserved = new Set([ + 'implements', + 'interface', + 'let', + 'package', + 'private', + 'protected', + 'public', + 'static', + 'yield', + 'enum' +]); + +export default function parseCJS(source, name = '@') { + resetState(); + try { + parseSource(source); + } catch (e) { + e.message += `\n at ${name}:${source.slice(0, pos).split('\n').length}:${pos - source.lastIndexOf('\n', pos - 1)}`; + e.loc = pos; + throw e; + } + const result = { exports: [..._exports], reexports: [...reexports], requires }; + resetState(); + return result; +} + +function addExport(name) { + if (!strictReserved.has(name)) _exports.add(name); +} + +function parseSource(cjsSource) { + source = cjsSource; + pos = -1; + end = source.length - 1; + let ch = 0; + + // Handle #! + if (source.charCodeAt(0) === 35 /*#*/ && source.charCodeAt(1) === 33 /*!*/) { + if (source.length === 2) return true; + pos += 2; + while (pos++ < end) { + ch = source.charCodeAt(pos); + if (ch === 10 /*\n*/ || ch === 13 /*\r*/) break; + } + } + + while (pos++ < end) { + ch = source.charCodeAt(pos); + + if (ch === 32 || (ch < 14 && ch > 8)) continue; + + if (openTokenDepth === 0) { + switch (ch) { + case 105 /*i*/: + if (source.startsWith('mport', pos + 1) && keywordStart(pos)) throwIfImportStatement(); + lastTokenPos = pos; + continue; + case 114 /*r*/: + const startPos = pos; + if (tryParseRequire(false) && keywordStart(startPos)) tryBacktrackAddStarExportBinding(startPos - 1); + lastTokenPos = pos; + continue; + case 95 /*_*/: + if (source.startsWith('_export', pos + 1) && (keywordStart(pos) || source.charCodeAt(pos - 1) === 46) /*.*/) { + pos += 8; + if (source.startsWith('Star', pos)) pos += 4; + if (source.charCodeAt(pos) === 40 /*(*/) { + openTokenPosStack[openTokenDepth++] = lastTokenPos; + if (source.charCodeAt(++pos) === 114 /*r*/) tryParseRequire(true); + } + } + lastTokenPos = pos; + continue; + } + } + + switch (ch) { + case 101 /*e*/: + if (source.startsWith('xport', pos + 1) && keywordStart(pos)) { + if (source.charCodeAt(pos + 6) === 115 /*s*/) tryParseExportsDotAssign(false); + else if (openTokenDepth === 0) throwIfExportStatement(); + } + break; + case 99 /*c*/: + if (keywordStart(pos) && source.startsWith('lass', pos + 1) && isBrOrWs(source.charCodeAt(pos + 5))) + nextBraceIsClass = true; + break; + case 109 /*m*/: + if (source.startsWith('odule', pos + 1) && keywordStart(pos)) tryParseModuleExportsDotAssign(); + break; + case 79 /*O*/: + if (source.startsWith('bject', pos + 1) && keywordStart(pos)) tryParseObjectDefineOrKeys(openTokenDepth === 0); + break; + case 40 /*(*/: + openTokenPosStack[openTokenDepth++] = lastTokenPos; + break; + case 41 /*)*/: + if (openTokenDepth === 0) throw new Error('Unexpected closing bracket.'); + openTokenDepth--; + break; + case 123 /*{*/: + openClassPosStack[openTokenDepth] = nextBraceIsClass; + nextBraceIsClass = false; + openTokenPosStack[openTokenDepth++] = lastTokenPos; + break; + case 125 /*}*/: + if (openTokenDepth === 0) throw new Error('Unexpected closing brace.'); + if (openTokenDepth-- === templateDepth) { + templateDepth = templateStack[--templateStackDepth]; + templateString(); + } else { + if (templateDepth !== -1 && openTokenDepth < templateDepth) throw new Error('Unexpected closing brace.'); + } + break; + case 60 /*>*/: + // TODO: import token } for (const item of exports) { - // TODO: this is just the exported name, how will we use this to transform anything without doing a textual lookup... - console.log('export', item); + code = code.replace(`exports.${item} =`, `export const ${item} =`); } // TODO: this is not picked up by cjs-module-lexer @@ -40,5 +35,5 @@ export async function transformRequires(code, id) { code = code.replace('module.exports =', 'export default'); } - return out; + return code; }