From ec18017851aa57800491b11329d4c426fee752b1 Mon Sep 17 00:00:00 2001 From: EGOIST Date: Mon, 23 Dec 2024 01:20:42 +0800 Subject: [PATCH] feat: new compileTime api --- .editorconfig | 12 ++ .github/workflows/ci.yml | 5 +- README.md | 66 +----- client.d.ts | 6 +- example/a.ts | 20 ++ example/b.ts | 1 + example/dep.ts | 2 +- example/generate-code.ts | 5 - example/get-data.ts | 10 - example/main.ts | 9 +- example/message.txt | 1 + inject.js | 1 + package.json | 21 +- pnpm-lock.yaml | 425 +++++++++++++++++++++++++++++++-------- src/index.ts | 86 +++----- src/transformer.ts | 188 +++++++++++++++++ test/dep.ts | 1 - test/fixture-code.ts | 5 - test/fixture-data.ts | 10 - test/fixture.ts | 3 + test/index.test.ts | 18 +- 21 files changed, 623 insertions(+), 272 deletions(-) create mode 100644 .editorconfig create mode 100644 example/a.ts create mode 100644 example/b.ts delete mode 100644 example/generate-code.ts delete mode 100644 example/get-data.ts create mode 100644 example/message.txt create mode 100644 inject.js create mode 100644 src/transformer.ts delete mode 100644 test/dep.ts delete mode 100644 test/fixture-code.ts delete mode 100644 test/fixture-data.ts create mode 100644 test/fixture.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ebe51d3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9fbdd46..0fb0808 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,12 +8,13 @@ on: jobs: test: - if: "!contains(github.event.head_commit.message, 'ci skip')" + if: >- + !contains(github.event.head_commit.message, 'ci skip') strategy: matrix: os: [ubuntu-latest, windows-latest] - node-version: [18.x] + node-version: [22.x] runs-on: ${{ matrix.os }} diff --git a/README.md b/README.md index f8ff8d7..3ef3a5c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![npm version](https://badgen.net/npm/v/vite-plugin-compile-time?v=2)](https://npm.im/vite-plugin-compile-time) [![npm downloads](https://badgen.net/npm/dm/vite-plugin-compile-time?v=2)](https://npm.im/vite-plugin-compile-time) -Use this plugin to generate code at compile time or get data at compile time in your Vite projects. +Use this plugin to get data at compile time in your Vite projects. ## Install @@ -27,12 +27,12 @@ export default defineConfig({ In **tsconfig.json**: -```json5 +```json { "compilerOptions": { // ... "types": [ - // ..., + // ..., "vite-plugin-compile-time/client" ] } @@ -44,73 +44,25 @@ In **tsconfig.json**: Compile-time data: ```ts -// get-data.ts import fs from "fs" -export default async () => { +const post = compileTime(async () => { const post = await fs.promises.readFile("./post.md", "utf8") return { data: { post }, } -} - -// get the data at compile time -const data = import.meta.compileTime("./get-data.ts") -assert.deepEqual(data, { post: "....." }) -``` - -Compile-time code: - -```ts -// generate-code.ts -export default async () => { - return { - code: `count++`, - } -} - -// insert the generated code at compile time -let count = 0 -import.meta.compileTime("./generate-code.ts") -assert.equal(count, 1) -``` - -## API - -Use `import.meta.compileTime` to get compile-time data or code. +}) -```ts -declare interface ImportMeta { - compileTime: (file: string) => T -} +assert.equal(post, "...the content of the post...") ``` -You should return a default export with object containing `code` or `data` property: - -```ts -import { - CompileTimeFunctionArgs, - CompileTimeFunctionResult, -} from "vite-plugin-compile-time" - -export default async ( - args: CompileTimeFunctionArgs, -): CompileTimeFunctionResult => { - return { - data: { - hello: "world", - }, - // Trigger rebuild when watched files change - watchFiles: ["/absolute/path"], - } -} -``` +## Caveats -See the type docs on [paka.dev](https://paka.dev/npm/vite-plugin-compile-time#module-index-export-CompileTimeFunction). +The files where you call `compileTime` will be evaluated at build time in Node.js environment, which means you should avoid calling browser APIs on the top level. It's recommended to use `compileTime` in a separate file and import it in your app. ## Sponsors -[![sponsors](https://sponsors-images.egoist.sh/sponsors.svg)](https://github.com/sponsors/egoist) +[![sponsors](https://sponsors-images.egoist.dev/sponsors.svg)](https://github.com/sponsors/egoist) ## License diff --git a/client.d.ts b/client.d.ts index 6e62801..c72c3ca 100644 --- a/client.d.ts +++ b/client.d.ts @@ -1,3 +1,3 @@ -declare interface ImportMeta { - compileTime: (id: string) => T -} +type MaybePromise = T | Promise + +declare const compileTime: (fn: () => MaybePromise) => T diff --git a/example/a.ts b/example/a.ts new file mode 100644 index 0000000..23949b5 --- /dev/null +++ b/example/a.ts @@ -0,0 +1,20 @@ +import fs from "fs" +import path from "path" +import { sum } from "./dep" + +export const a = compileTime(async () => { + const message = await fs.promises.readFile( + path.join(__dirname, "message.txt"), + "utf-8", + ) + return { + message, + count: sum(1, 25), + } +}) + +const another = compileTime(async () => { + return "another" +}) + +console.log("build", another) diff --git a/example/b.ts b/example/b.ts new file mode 100644 index 0000000..875db29 --- /dev/null +++ b/example/b.ts @@ -0,0 +1 @@ +export const b = "b" diff --git a/example/dep.ts b/example/dep.ts index 059895c..786a1e8 100644 --- a/example/dep.ts +++ b/example/dep.ts @@ -1 +1 @@ -export const sum = (a: number, b: number) => a + b +export const sum = (a: number, b: number) => a + b + 1 diff --git a/example/generate-code.ts b/example/generate-code.ts deleted file mode 100644 index 052be82..0000000 --- a/example/generate-code.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default () => { - return { - code: `count+=2`, - } -} diff --git a/example/get-data.ts b/example/get-data.ts deleted file mode 100644 index 757bf3f..0000000 --- a/example/get-data.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { sum } from "./dep" - -export default () => { - return { - data: { - a: "as2", - num: sum(1, 2), - }, - } -} diff --git a/example/main.ts b/example/main.ts index a993e91..eed227f 100644 --- a/example/main.ts +++ b/example/main.ts @@ -1,7 +1,4 @@ -const data = import.meta.compileTime("./get-data.ts") +import { a } from "./a" +import { b } from "./b" -let count = 40 - -import.meta.compileTime("./generate-code.ts") - -console.log(data, count) +console.log(a, b) diff --git a/example/message.txt b/example/message.txt new file mode 100644 index 0000000..bc7774a --- /dev/null +++ b/example/message.txt @@ -0,0 +1 @@ +hello world! \ No newline at end of file diff --git a/inject.js b/inject.js new file mode 100644 index 0000000..80afd2c --- /dev/null +++ b/inject.js @@ -0,0 +1 @@ +export const compileTime = (fn) => fn() diff --git a/package.json b/package.json index b7535bb..87b8a12 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,22 @@ { "name": "vite-plugin-compile-time", "version": "0.3.2", + "type": "module", "description": "Do some compile time work in your Vite project", "publishConfig": { "access": "public" }, "files": [ "dist", - "/client.d.ts" + "/client.d.ts", + "/inject.js" ], - "main": "./dist/index.js", - "module": "./dist/index.mjs", + "main": "./dist/index.cjs", + "module": "./dist/index.js", "exports": { ".": { - "require": "./dist/index.js", - "import": "./dist/index.mjs" + "require": "./dist/index.cjs", + "import": "./dist/index.js" }, "./client": { "types": "./client.d.ts" @@ -30,8 +32,9 @@ "license": "MIT", "devDependencies": { "@egoist/prettier-config": "1.0.0", + "@types/babel__generator": "^7.6.8", + "@types/babel__traverse": "^7.20.6", "prettier": "3.0.1", - "sucrase": "3.34.0", "tsup": "8.3.5", "typescript": "5.7.2", "vite": "6.0.1", @@ -39,7 +42,11 @@ "vitest": "2.1.6" }, "dependencies": { - "bundle-require": "^5.0.0", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", + "@babel/traverse": "^7.26.4", + "babel-dead-code-elimination": "^1.0.8", + "bundle-require": "^5.1.0", "devalue": "^5.1.1", "esbuild": "^0.24.0", "magic-string": "^0.30.14" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37ea521..83dcb6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,21 @@ importers: .: dependencies: + '@babel/generator': + specifier: ^7.26.3 + version: 7.26.3 + '@babel/parser': + specifier: ^7.26.3 + version: 7.26.3 + '@babel/traverse': + specifier: ^7.26.4 + version: 7.26.4 + babel-dead-code-elimination: + specifier: ^1.0.8 + version: 1.0.8 bundle-require: - specifier: ^5.0.0 - version: 5.0.0(esbuild@0.24.0) + specifier: ^5.1.0 + version: 5.1.0(esbuild@0.24.0) devalue: specifier: ^5.1.1 version: 5.1.1 @@ -24,12 +36,15 @@ importers: '@egoist/prettier-config': specifier: 1.0.0 version: 1.0.0 + '@types/babel__generator': + specifier: ^7.6.8 + version: 7.6.8 + '@types/babel__traverse': + specifier: ^7.20.6 + version: 7.20.6 prettier: specifier: 3.0.1 version: 3.0.1 - sucrase: - specifier: 3.34.0 - version: 3.34.0 tsup: specifier: 8.3.5 version: 8.3.5(postcss@8.4.49)(typescript@5.7.2) @@ -48,6 +63,73 @@ importers: packages: + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.26.3': + resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.26.3': + resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.26.3': + resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.26.4': + resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.3': + resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} + engines: {node: '>=6.9.0'} + '@egoist/prettier-config@1.0.0': resolution: {integrity: sha512-D1Tp9Jv4aVoEo3cOAO966gFCI0wx/1lZ6sEHX8uMAfyVxuxD2+knGxhfGlb/FNLxWV3ifSI5hOmB/zFfsi7Rzw==} @@ -203,6 +285,10 @@ packages: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + '@jridgewell/resolve-uri@3.1.1': resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} @@ -211,6 +297,10 @@ packages: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} @@ -220,6 +310,9 @@ packages: '@jridgewell/trace-mapping@0.3.19': resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -314,6 +407,12 @@ packages: cpu: [x64] os: [win32] + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -372,17 +471,22 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + babel-dead-code-elimination@1.0.8: + resolution: {integrity: sha512-og6HQERk0Cmm+nTT4Od2wbPtgABXFMPaHACjbKLulZIFMkYyXZLkUGuAxdgpMJBrxyt/XFpSz++lNzjbcMnPkQ==} + balanced-match@1.0.0: resolution: {integrity: sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - bundle-require@5.0.0: - resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} + browserslist@4.24.3: + resolution: {integrity: sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.18' @@ -391,6 +495,9 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + caniuse-lite@1.0.30001690: + resolution: {integrity: sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==} + chai@5.1.2: resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} engines: {node: '>=12'} @@ -414,13 +521,13 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - consola@3.2.3: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -444,6 +551,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.75: + resolution: {integrity: sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -458,6 +568,10 @@ packages: engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -477,30 +591,26 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - glob@7.1.6: - resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -515,6 +625,19 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + lilconfig@3.1.2: resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} engines: {node: '>=14'} @@ -535,12 +658,12 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.14: resolution: {integrity: sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==} - minimatch@3.0.4: - resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -564,20 +687,16 @@ packages: resolution: {integrity: sha512-JMaRS9L4wSRIR+6PTVEikTrq/lMGEZR43a48ETeilY0Q0iMwVnccMFrUM1k+tNzmYuIU0Vh710bCUqHX+/+ctQ==} engines: {node: '>=0.10.0'} + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -648,6 +767,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -693,11 +816,6 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - sucrase@3.34.0: - resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} - engines: {node: '>=8'} - hasBin: true - sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -776,6 +894,12 @@ packages: engines: {node: '>=14.17'} hasBin: true + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + vite-node@2.1.6: resolution: {integrity: sha512-DBfJY0n9JUwnyLxPSSUmEePT21j8JZp/sR9n+/gBwQU6DcQOioPdb8/pibWfXForbirSagZCilseYIwaL3f95A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -878,11 +1002,114 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} snapshots: + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.26.3': {} + + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.3 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.3 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.3': + dependencies: + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.25.9': + dependencies: + '@babel/compat-data': 7.26.3 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.3 + + '@babel/parser@7.26.3': + dependencies: + '@babel/types': 7.26.3 + + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 + + '@babel/traverse@7.26.4': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.3 + '@babel/parser': 7.26.3 + '@babel/template': 7.25.9 + '@babel/types': 7.26.3 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.26.3': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@egoist/prettier-config@1.0.0': {} '@esbuild/aix-ppc64@0.24.0': @@ -972,10 +1199,18 @@ snapshots: '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.19 + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri@3.1.1': {} '@jridgewell/set-array@1.1.2': {} + '@jridgewell/set-array@1.2.1': {} + '@jridgewell/sourcemap-codec@1.4.15': {} '@jridgewell/sourcemap-codec@1.5.0': {} @@ -985,6 +1220,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@pkgjs/parseargs@0.11.0': optional: true @@ -1042,6 +1282,14 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.28.0': optional: true + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.26.3 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.26.3 + '@types/estree@1.0.6': {} '@types/node@20.4.9': @@ -1101,24 +1349,37 @@ snapshots: assertion-error@2.0.1: {} + babel-dead-code-elimination@1.0.8: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.26.3 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + balanced-match@1.0.0: {} - brace-expansion@1.1.11: + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.0 - concat-map: 0.0.1 - brace-expansion@2.0.1: + browserslist@4.24.3: dependencies: - balanced-match: 1.0.0 + caniuse-lite: 1.0.30001690 + electron-to-chromium: 1.5.75 + node-releases: 2.0.19 + update-browserslist-db: 1.1.1(browserslist@4.24.3) - bundle-require@5.0.0(esbuild@0.24.0): + bundle-require@5.1.0(esbuild@0.24.0): dependencies: esbuild: 0.24.0 load-tsconfig: 0.2.5 cac@6.7.14: {} + caniuse-lite@1.0.30001690: {} + chai@5.1.2: dependencies: assertion-error: 2.0.1 @@ -1141,10 +1402,10 @@ snapshots: commander@4.1.1: {} - concat-map@0.0.1: {} - consola@3.2.3: {} + convert-source-map@2.0.0: {} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -1161,6 +1422,8 @@ snapshots: eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.75: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -1194,6 +1457,8 @@ snapshots: '@esbuild/win32-ia32': 0.24.0 '@esbuild/win32-x64': 0.24.0 + escalade@3.2.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.6 @@ -1209,11 +1474,11 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true + gensync@1.0.0-beta.2: {} + glob@10.4.5: dependencies: foreground-child: 3.3.0 @@ -1223,24 +1488,10 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@7.1.6: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.0.4 - once: 1.4.0 - path-is-absolute: 1.0.1 + globals@11.12.0: {} globrex@0.1.2: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - is-fullwidth-code-point@3.0.0: {} isexe@2.0.0: {} @@ -1253,6 +1504,12 @@ snapshots: joycon@3.1.1: {} + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json5@2.2.3: {} + lilconfig@3.1.2: {} lines-and-columns@1.1.6: {} @@ -1265,13 +1522,13 @@ snapshots: lru-cache@10.4.3: {} - magic-string@0.30.14: + lru-cache@5.1.1: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + yallist: 3.1.1 - minimatch@3.0.4: + magic-string@0.30.14: dependencies: - brace-expansion: 1.1.11 + '@jridgewell/sourcemap-codec': 1.5.0 minimatch@9.0.5: dependencies: @@ -1291,16 +1548,12 @@ snapshots: node-modules-regexp@1.0.0: {} - object-assign@4.1.1: {} + node-releases@2.0.19: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 + object-assign@4.1.1: {} package-json-from-dist@1.0.1: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} path-scurry@1.11.1: @@ -1364,6 +1617,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.28.0 fsevents: 2.3.3 + semver@6.3.1: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -1404,16 +1659,6 @@ snapshots: dependencies: ansi-regex: 6.1.0 - sucrase@3.34.0: - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - commander: 4.1.1 - glob: 7.1.6 - lines-and-columns: 1.1.6 - mz: 2.7.0 - pirates: 4.0.1 - ts-interface-checker: 0.1.13 - sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.3 @@ -1461,7 +1706,7 @@ snapshots: tsup@8.3.5(postcss@8.4.49)(typescript@5.7.2): dependencies: - bundle-require: 5.0.0(esbuild@0.24.0) + bundle-require: 5.1.0(esbuild@0.24.0) cac: 6.7.14 chokidar: 4.0.1 consola: 3.2.3 @@ -1488,6 +1733,12 @@ snapshots: typescript@5.7.2: {} + update-browserslist-db@1.1.1(browserslist@4.24.3): + dependencies: + browserslist: 4.24.3 + escalade: 3.2.0 + picocolors: 1.1.1 + vite-node@2.1.6(@types/node@20.4.9): dependencies: cac: 6.7.14 @@ -1596,4 +1847,4 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 - wrappy@1.0.2: {} + yallist@3.1.1: {} diff --git a/src/index.ts b/src/index.ts index 86baaf6..feee03f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,6 @@ import path from "path" import { Plugin } from "vite" -import MagicString from "magic-string" -import { bundleRequire } from "bundle-require" +import { Transformer } from "./transformer" type MaybePromise = T | Promise @@ -29,14 +28,18 @@ const createPlugins = (): Plugin[] => { string, { data?: any; code?: string; watchFiles?: string[] } > = new Map() - let root = process.cwd() + + const transformer = new Transformer() return [ { name: "compile-time", enforce: "pre", + buildStart() { + transformer.reset() + }, + configResolved(config) { useSourceMap = !!config.build.sourcemap - root = config.root }, configureServer(server) { server.watcher.on("all", (_, id) => { @@ -48,71 +51,28 @@ const createPlugins = (): Plugin[] => { }) }, async transform(code, id) { - if ( - id.includes("node_modules") || - !/\.(js|ts|jsx|tsx|mjs|vue|svelte)$/.test(id) - ) - return - - const m = [ - ...code.matchAll( - /import\.meta\.compileTime(?:<[\w]*>)?\([\n\s]*['"`]([^'"`]+)['"`],?[\n\s]*\)/g, - ), - ] - - if (m.length === 0) return - - const devalue = await import("devalue") - const s = new MagicString(code) - - for (const item of m) { - const start = item.index! - const end = item.index! + item[0].length - - const resolved = await this.resolve(item[1], id) - - if (!resolved) { - throw new Error('cannot resolve "' + item[1] + '"') - } - - const filepath = resolved.id + return transformer.insertPlaceholders(code, id, { useSourceMap }) + }, + }, - const cacheKey = filepath - let cache = loadCache.get(cacheKey) - if (!cache) { - const { mod, dependencies } = await bundleRequire({ filepath }) - const defaultExport: CompileTimeFunction | undefined = - mod.default || mod - cache = (defaultExport && (await defaultExport({ root }))) || {} + { + name: "compile-time:run-code", + enforce: "pre", - cache.watchFiles = [ - filepath, - ...(cache.watchFiles || []), - ...dependencies.map((p) => path.resolve(p)), - ] - if (cache.data) { - cache.data = devalue.uneval(cache.data) - } - loadCache.set(cacheKey, cache) - } + async transform(_, id) { + const result = await transformer.replaceWithData(id, { useSourceMap }) - let replacement = "null" - if (cache.watchFiles) { - cache.watchFiles.forEach((filepath) => { - this.addWatchFile(filepath) - }) - } - if (cache.data !== undefined) { - replacement = cache.data - } else if (cache.code !== undefined) { - replacement = cache.code - } + if (!result) return - s.overwrite(start, end, replacement) + if (result.dependencies) { + result.dependencies.forEach((filepath) => { + this.addWatchFile(path.resolve(filepath)) + }) } + return { - code: s.toString(), - map: useSourceMap ? s.generateMap({ source: id }) : null, + code: result.code, + map: result.map, } }, }, diff --git a/src/transformer.ts b/src/transformer.ts new file mode 100644 index 0000000..24dbcda --- /dev/null +++ b/src/transformer.ts @@ -0,0 +1,188 @@ +import fs from "fs" +import * as devalue from "devalue" +import { parse } from "@babel/parser" +import path from "path" +import { createRequire } from "module" +import { + deadCodeElimination, + findReferencedIdentifiers, +} from "babel-dead-code-elimination" +import { bundleRequire } from "bundle-require" +import MagicString from "magic-string" +import { fileURLToPath } from "url" + +const req = createRequire(import.meta.url) + +const DIRNAME = + typeof __dirname === "undefined" + ? path.dirname(fileURLToPath(import.meta.url)) + : __dirname + +const extensionsRe = /\.(([jt]sx?)|mjs|cjs|mts|cts|vue|astro|svelte)$/ +type Match = { name: string; start: number; end: number } + +export class Transformer { + cache: Map = new Map() + files: Map = new Map() + matches: Map = new Map() + + reset() { + this.cache.clear() + this.files.clear() + this.matches.clear() + } + + async insertPlaceholders( + code: string, + filepath: string, + { useSourceMap }: { useSourceMap: boolean }, + ) { + this.matches.delete(filepath) + + if (!code.includes("compileTime(") || !extensionsRe.test(filepath)) { + return + } + + const matches: Match[] = [] + this.files.set(filepath, code) + + const ast = parse(code, { sourceType: "module" }) + + const referenced = findReferencedIdentifiers(ast) + + const traverse = req("@babel/traverse") as typeof import("@babel/traverse") + const generator = req( + "@babel/generator", + ) as typeof import("@babel/generator") + + traverse.default(ast, { + CallExpression(path) { + if ( + path.node.callee.type === "Identifier" && + path.node.callee.name === "compileTime" && + path.parent.type !== "AwaitExpression" + ) { + const parent = path.parent + + if ( + parent.type !== "VariableDeclarator" || + parent.id.type !== "Identifier" + ) { + throw new Error( + `missing assignment, compileTime must be used as export const foo = compileTime(...)`, + ) + } + + const name = parent.id.name + + const start = path.node.start! + const end = path.node.end! + + matches.push({ + name, + start, + end, + }) + + path.replaceWith({ + type: "Identifier", + name: "null", + }) + } + }, + }) + + deadCodeElimination(ast, referenced) + + const result = generator.default(ast, { + sourceFileName: filepath, + sourceMaps: useSourceMap, + }) + + this.matches.set(filepath, matches) + + return { + code: result.code, + map: result.map, + } + } + + async replaceWithData( + filepath: string, + { useSourceMap }: { useSourceMap: boolean }, + ) { + if (!extensionsRe.test(filepath)) { + return + } + + const content = this.files.get(filepath) + if (!content) { + return + } + + const matches = this.matches.get(filepath) || [] + + if (matches.length === 0) { + return + } + + const exportName = "__compile_time_data__" + const { mod, dependencies } = await bundleRequire({ + filepath, + readFile(_filepath) { + const content = fs.readFileSync(_filepath, "utf-8") + + if (_filepath === filepath) { + // add await prefix + const s = new MagicString(content) + s.prepend(`\n\nexport const ${exportName} = {};`) + + for (const m of matches) { + s.prependLeft( + m.start, + `${exportName}[${JSON.stringify(m.name)}] = await `, + ) + } + return s.toString() + } + + return content + }, + esbuildOptions: { + inject: [path.join(DIRNAME, "../inject.js")], + }, + }) + + const values: Record = mod[exportName] + + const s = new MagicString(content) + for (const m of matches) { + const start = m.start + const end = m.end + + let value = values[m.name] + let replacement = "" + + if (value instanceof Response) { + const str = devalue.uneval(await value.arrayBuffer()) + replacement = `new Response(${str})` + } else { + replacement = devalue.uneval(value) + } + + s.overwrite(start, end, replacement) + } + + if (useSourceMap) { + return { + code: s.toString(), + map: s.generateMap({ hires: true }), + } + } + + return { + code: s.toString(), + dependencies, + } + } +} diff --git a/test/dep.ts b/test/dep.ts deleted file mode 100644 index 059895c..0000000 --- a/test/dep.ts +++ /dev/null @@ -1 +0,0 @@ -export const sum = (a: number, b: number) => a + b diff --git a/test/fixture-code.ts b/test/fixture-code.ts deleted file mode 100644 index b2d9217..0000000 --- a/test/fixture-code.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default () => { - return { - code: `count++`, - } -} diff --git a/test/fixture-data.ts b/test/fixture-data.ts deleted file mode 100644 index dfa966b..0000000 --- a/test/fixture-data.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { sum } from "./dep" - -export default () => { - return { - data: { - a: "a", - num: sum(1, 2), - }, - } -} diff --git a/test/fixture.ts b/test/fixture.ts new file mode 100644 index 0000000..cd663de --- /dev/null +++ b/test/fixture.ts @@ -0,0 +1,3 @@ +export const data = compileTime(async () => { + return "hi" +}) diff --git a/test/index.test.ts b/test/index.test.ts index 4ce28a4..b4ae218 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,18 +1,6 @@ import { assert, test } from "vitest" +import { data } from "./fixture" -test("compile time data", () => { - // prettier-ignore - const res = import.meta.compileTime( - "./fixture-data.ts" - ) - assert.deepEqual(res, { - a: "a", - num: 3, - }) -}) - -test("compile time code", () => { - let count = 0 - import.meta.compileTime("~/test/fixture-code.ts") - assert.equal(count, 1) +test("compile time", () => { + assert.equal(data, "hi") })