From 88adaa9ba44c0a79613c539e57b060f1e046eca3 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Mon, 15 Apr 2024 12:40:36 +0800 Subject: [PATCH] transform non-default imports and exports using babel since they are not supported by njs. polyfill also added --- _src/babel-njs/hooks.mjs | 33 +++++++++++ _src/babel-njs/index.mjs | 88 ++++++++++++++++++++++++++++ _src/entrypoints-njs/legacy_index.js | 1 + _src/lib/njs-polyfill.js | 1 + _vite.config.mjs | 1 + 5 files changed, 124 insertions(+) create mode 100644 _src/babel-njs/hooks.mjs create mode 100644 _src/babel-njs/index.mjs create mode 100644 _src/lib/njs-polyfill.js diff --git a/_src/babel-njs/hooks.mjs b/_src/babel-njs/hooks.mjs new file mode 100644 index 0000000..e8fd2c9 --- /dev/null +++ b/_src/babel-njs/hooks.mjs @@ -0,0 +1,33 @@ +const commonJSHooksKey = + "transform-njs-module/customWrapperPlugin"; + +export function defineCommonJSHook(file, hook) { + let hooks = file.get(commonJSHooksKey); + if (!hooks) file.set(commonJSHooksKey, (hooks = [])); + hooks.push(hook); +} + +function findMap(arr, cb) { + if (arr) { + for (const el of arr) { + const res = cb(el); + if (res != null) return res; + } + } +} + +export function makeInvokers(file){ + const hooks = file.get(commonJSHooksKey); + + return { + getWrapperPayload(...args) { + return findMap(hooks, hook => hook.getWrapperPayload?.(...args)); + }, + wrapReference(...args) { + return findMap(hooks, hook => hook.wrapReference?.(...args)); + }, + buildRequireWrapper(...args) { + return findMap(hooks, hook => hook.buildRequireWrapper?.(...args)); + }, + }; +} diff --git a/_src/babel-njs/index.mjs b/_src/babel-njs/index.mjs new file mode 100644 index 0000000..705c017 --- /dev/null +++ b/_src/babel-njs/index.mjs @@ -0,0 +1,88 @@ +import { declare } from "@babel/helper-plugin-utils"; +import { + isModule, + rewriteModuleStatementsAndPrepareHeader, + isSideEffectImport, + buildNamespaceInitStatements, + ensureStatementsHoisted, + wrapInterop, + getModuleName, +} from "@babel/helper-module-transforms"; + +import { template, types as t } from "@babel/core"; + +import { makeInvokers } from "./hooks.mjs"; + +export default declare((api) => { + return { + name: "transform-njs-module", + + visitor: { + Program: { + exit(path, state) { + if (!isModule(path)) return; + + // Rename the bindings auto-injected into the scope so there is no + // risk of conflict between the bindings. + path.scope.rename("exports"); + + let moduleName = getModuleName(this.file.opts, {}); + // @ts-expect-error todo(flow->ts): do not reuse variables + if (moduleName) moduleName = t.stringLiteral(moduleName); + + const hooks = makeInvokers(this.file); + + const { meta, headers } = rewriteModuleStatementsAndPrepareHeader( + path, + { + exportName: "exports", + constantReexports: true, + enumerableModuleMeta: true, + strict: true, + strictMode: true, + allowTopLevelThis: true, + importInterop: "none", + wrapReference: hooks.wrapReference, + getWrapperPayload: hooks.getWrapperPayload, + esNamespaceOnly: true, + filename: this.file.opts.filename, + }, + ); + + headers.unshift(...template.statements.ast(` + const exports = {}; + export default exports; + `)); + + for (const [source, metadata] of meta.source) { + let header; + if (isSideEffectImport(metadata)) { + header = t.importDeclaration([], t.stringLiteral(source)); + } else { + header = t.importDeclaration([t.importDefaultSpecifier(t.identifier(metadata.name))], t.stringLiteral(source)); + } + header.loc = metadata.loc; + + headers.push(header); + headers.push( + ...buildNamespaceInitStatements( + meta, + metadata, + hooks.wrapReference, + ), + ); + } + + ensureStatementsHoisted(headers); + path.unshiftContainer("body", headers); + path.get("body").forEach(path => { + if (headers.indexOf(path.node) === -1) return; + if (path.isVariableDeclaration()) { + path.scope.registerDeclaration(path); + } + }); + }, + }, + }, + }; +}); diff --git a/_src/entrypoints-njs/legacy_index.js b/_src/entrypoints-njs/legacy_index.js index 155debb..50311f7 100644 --- a/_src/entrypoints-njs/legacy_index.js +++ b/_src/entrypoints-njs/legacy_index.js @@ -1,3 +1,4 @@ +import "../lib/njs-polyfill.js"; import Mark from "markup-js"; import processingHandlers from "../lib/mirrorListDataProcessing"; import { TUNASYNC_JSON_PATH } from "../lib/consts"; diff --git a/_src/lib/njs-polyfill.js b/_src/lib/njs-polyfill.js new file mode 100644 index 0000000..7703116 --- /dev/null +++ b/_src/lib/njs-polyfill.js @@ -0,0 +1 @@ +import "core-js/modules/es.object.from-entries"; diff --git a/_vite.config.mjs b/_vite.config.mjs index 56cb503..f8b19e1 100644 --- a/_vite.config.mjs +++ b/_vite.config.mjs @@ -182,6 +182,7 @@ export default defineConfig(({ mode }) => ({ "babel-preset-njs", ], plugins: [ + "./_src/babel-njs/index.mjs", ], configFile: false, }),