From 060b549514639cb9abd704e191f8b48f774b2719 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 27 Aug 2024 12:21:58 +0800 Subject: [PATCH] feat: add `$prepend` and `$append` APIs to `imports` --- src/helpers/vite.ts | 2 +- src/proxy/imports.ts | 28 +++++++++++++++++++++++----- src/proxy/types.ts | 3 +++ test/function-call.test.ts | 4 ++-- test/imports.test.ts | 30 +++++++++++++++++++++++++----- 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/helpers/vite.ts b/src/helpers/vite.ts index 297bd2a..9d5c1f7 100644 --- a/src/helpers/vite.ts +++ b/src/helpers/vite.ts @@ -61,7 +61,7 @@ export function addVitePlugin( insertPluginIntoConfig(plugin, config); } - magicast.imports.$add({ + magicast.imports.$prepend({ from: plugin.from, local: plugin.constructor, imported: plugin.imported || "default", diff --git a/src/proxy/imports.ts b/src/proxy/imports.ts index 7b4cb99..2749368 100644 --- a/src/proxy/imports.ts +++ b/src/proxy/imports.ts @@ -131,7 +131,11 @@ export function createImportsProxy( return imports; }; - const updateImport = (key: string, value: ImportItemInput) => { + const updateImport = ( + key: string, + value: ImportItemInput, + order: "prepend" | "append", + ) => { const imports = getAllImports(); const item = imports.find((i) => i.local === key); const local = value.local || key; @@ -156,12 +160,20 @@ export function createImportsProxy( (i) => i.from === value.from, )?.$declaration; if (declaration) { - // TODO: insert after the last import maybe? declaration.specifiers.push(specifier as any); - } else { + } else if (order === "prepend" || imports.length === 0) { root.body.unshift( b.importDeclaration([specifier], b.stringLiteral(value.from)) as any, ); + } else { + // The `imports` length is already checked above, so `at(-1)` will exist + const lastImport = imports.at(-1)!.$declaration; + const lastImportIndex = root.body.indexOf(lastImport); + root.body.splice( + lastImportIndex + 1, + 0, + b.importDeclaration([specifier], b.stringLiteral(value.from)) as any, + ); } return true; }; @@ -185,7 +197,13 @@ export function createImportsProxy( { $type: "imports", $add(item: ImportItemInput) { - proxy[item.local || item.imported] = item as any; + updateImport(item.local || item.imported, item, "prepend"); + }, + $prepend(item: ImportItemInput) { + updateImport(item.local || item.imported, item, "prepend"); + }, + $append(item: ImportItemInput) { + updateImport(item.local || item.imported, item, "append"); }, get $items() { return getAllImports(); @@ -203,7 +221,7 @@ export function createImportsProxy( return getAllImports().find((i) => i.local === prop); }, set(_, prop, value) { - return updateImport(prop as string, value); + return updateImport(prop as string, value, "prepend"); }, deleteProperty(_, prop) { return removeImport(prop as string); diff --git a/src/proxy/types.ts b/src/proxy/types.ts index a194812..cbefc04 100644 --- a/src/proxy/types.ts +++ b/src/proxy/types.ts @@ -91,7 +91,10 @@ export type ProxifiedModule> = export type ProxifiedImportsMap = Record & ProxyBase & { $type: "imports"; + /** @deprecated Use `$prepend` instead */ $add: (item: ImportItemInput) => void; + $prepend: (item: ImportItemInput) => void; + $append: (item: ImportItemInput) => void; $items: ProxifiedImportItem[]; }; diff --git a/test/function-call.test.ts b/test/function-call.test.ts index 3bcf43c..0ee9e00 100644 --- a/test/function-call.test.ts +++ b/test/function-call.test.ts @@ -44,7 +44,7 @@ describe("function-calls", () => { const installVuePlugin = (mod: ProxifiedModule) => { // Inject export default if not exists if (!mod.exports.default) { - mod.imports.$add({ + mod.imports.$prepend({ imported: "defineConfig", from: "vite", }); @@ -58,7 +58,7 @@ describe("function-calls", () => { : mod.exports.default; // Inject vue plugin import - mod.imports.$add({ + mod.imports.$prepend({ imported: "default", local: "vuePlugin", from: "@vitejs/plugin-vue", diff --git a/test/imports.test.ts b/test/imports.test.ts index 8922bb0..a59cf93 100644 --- a/test/imports.test.ts +++ b/test/imports.test.ts @@ -84,28 +84,45 @@ foo: [] });" `); - mod.imports.$add({ + mod.imports.$prepend({ from: "foo", imported: "default", local: "Foo", }); - mod.imports.$add({ + mod.imports.$prepend({ from: "star", imported: "*", local: "Star", }); - mod.imports.$add({ + mod.imports.$prepend({ from: "vite", imported: "Good", }); + mod.imports.$append({ + from: "append-foo", + imported: "default", + local: "AppendFoo", + }); + mod.imports.$append({ + from: "append-star", + imported: "*", + local: "AppendStar", + }); + mod.imports.$append({ + from: "vite", + imported: "AppendGood", + }); expect(await generate(mod)).toMatchInlineSnapshot(` "import * as Star from "star"; import Foo from "foo"; - import { defineConfig, Good } from "vite"; + import { defineConfig, Good, AppendGood } from "vite"; import VuePlugin from "@vitejs/plugin-vue"; import * as path2 from "path"; + import AppendFoo from "append-foo"; + import * as AppendStar from "append-star"; + export default defineConfig({ foo: [], });" @@ -117,10 +134,13 @@ foo: [] "import { defineConfig } from "vitest/config"; import * as Star from "star"; import Foo from "foo"; - import { Good } from "vite"; + import { Good, AppendGood } from "vite"; import VuePlugin from "@vitejs/plugin-vue"; import * as path2 from "path"; + import AppendFoo from "append-foo"; + import * as AppendStar from "append-star"; + export default defineConfig({ foo: [], });"