Skip to content

Commit

Permalink
feat(helpers): handle Vite config objects declared as variables in `a…
Browse files Browse the repository at this point in the history
…ddVitePlugin` (#69)
  • Loading branch information
Lms24 authored Jul 27, 2023
1 parent 68ab627 commit 224eed2
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 17 deletions.
70 changes: 66 additions & 4 deletions src/helpers/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,69 @@
import type { ProxifiedModule } from "../types";
import { Program, VariableDeclarator } from "@babel/types";
import { generateCode, parseExpression } from "../code";
import { MagicastError } from "../error";
import type { Proxified, ProxifiedModule, ProxifiedObject } from "../types";

export function getDefaultExportOptions(magicast: ProxifiedModule<any>) {
return magicast.exports.default.$type === "function-call"
? magicast.exports.default.$args[0]
: magicast.exports.default;
return configFromNode(magicast.exports.default);
}

/**
* Returns the vite config object from a variable declaration thats
* exported as the default export.
*
* Example:
*
* ```js
* const config = {};
* export default config;
* ```
*
* @param magicast the module
*
* @returns an object containing the proxified config object and the
* declaration "parent" to attach the modified config to later.
* If no config declaration is found, undefined is returned.
*/
export function getConfigFromVariableDeclaration(
magicast: ProxifiedModule<any>
): {
declaration: VariableDeclarator;
config: ProxifiedObject<any> | undefined;
} {
if (magicast.exports.default.$type !== "identifier") {
throw new MagicastError(
`Not supported: Cannot modify this kind of default export (${magicast.exports.default.$type})`
);
}

const configDecalarationId = magicast.exports.default.$name;

for (const node of (magicast.$ast as Program).body) {
if (node.type === "VariableDeclaration") {
for (const declaration of node.declarations) {
if (
declaration.id.type === "Identifier" &&
declaration.id.name === configDecalarationId &&
declaration.init
) {
const init = declaration.init;

const configExpression = parseExpression(generateCode(init).code);

return {
declaration,
config: configFromNode(configExpression),
};
}
}
}
}
throw new MagicastError("Couldn't find config declaration");
}

function configFromNode(node: Proxified<any>): ProxifiedObject<any> {
if (node.$type === "function-call") {
return node.$args[0];
}
return node;
}
72 changes: 59 additions & 13 deletions src/helpers/vite.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { ProxifiedFunctionCall, ProxifiedModule } from "../proxy/types";
import type {
Proxified,
ProxifiedFunctionCall,
ProxifiedModule,
} from "../proxy/types";
import { builders } from "../builders";
import { getDefaultExportOptions } from "./config";
import { generateCode } from "../code";
import {
getConfigFromVariableDeclaration,
getDefaultExportOptions,
} from "./config";
import { deepMergeObject } from "./deep-merge";

export interface AddVitePluginOptions {
Expand Down Expand Up @@ -45,18 +53,13 @@ export function addVitePlugin(
magicast: ProxifiedModule<any>,
plugin: AddVitePluginOptions
) {
const config = getDefaultExportOptions(magicast);

const insertionIndex = plugin.index ?? config.plugins?.length ?? 0;
const config: Proxified | undefined = getDefaultExportOptions(magicast);

config.plugins ||= [];
config.plugins.splice(
insertionIndex,
0,
plugin.options
? builders.functionCall(plugin.constructor, plugin.options)
: builders.functionCall(plugin.constructor)
);
if (config.$type === "identifier") {
insertPluginIntoVariableDeclarationConfig(magicast, plugin);
} else {
insertPluginIntoConfig(plugin, config);
}

magicast.imports.$add({
from: plugin.from,
Expand Down Expand Up @@ -106,3 +109,46 @@ export function updateVitePluginConfig(

return true;
}

/**
* Insert @param plugin into a config object that's declared as a variable in
* the module (@param magicast).
*/
function insertPluginIntoVariableDeclarationConfig(
magicast: ProxifiedModule<any>,
plugin: AddVitePluginOptions
) {
const { config: configObject, declaration } =
getConfigFromVariableDeclaration(magicast);

insertPluginIntoConfig(plugin, configObject);

if (declaration.init) {
if (declaration.init.type === "ObjectExpression") {
// @ts-ignore this works despite the type error because of recast
declaration.init = generateCode(configObject).code;
} else if (
declaration.init.type === "CallExpression" &&
declaration.init.callee.type === "Identifier"
) {
// @ts-ignore this works despite the type error because of recast
declaration.init = generateCode(
builders.functionCall(declaration.init.callee.name, configObject)
).code;
}
}
}

function insertPluginIntoConfig(plugin: AddVitePluginOptions, config: any) {
const insertionIndex = plugin.index ?? config.plugins?.length ?? 0;

config.plugins ||= [];

config.plugins.splice(
insertionIndex,
0,
plugin.options
? builders.functionCall(plugin.constructor, plugin.options)
: builders.functionCall(plugin.constructor)
);
}
65 changes: 65 additions & 0 deletions test/helpers/vite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,69 @@ export default defineConfig({
});"
`);
});

it("handles default export from identifier (fn call)", () => {
const code = `
import { defineConfig } from 'vite';
import { somePlugin1, somePlugin2 } from 'some-module'
const config = defineConfig({
plugins: [somePlugin1(), somePlugin2()]
});
export default config;
`;

const mod = parseModule(code);

addVitePlugin(mod, {
from: "vite-plugin-pwa",
imported: "VitePWA",
constructor: "VitePWA",
});

expect(generate(mod)).toMatchInlineSnapshot(`
"import { VitePWA } from \\"vite-plugin-pwa\\";
import { defineConfig } from \\"vite\\";
import { somePlugin1, somePlugin2 } from \\"some-module\\";
const config = defineConfig({
plugins: [somePlugin1(), somePlugin2(), VitePWA()],
});
export default config;"
`);
});

it("handles default export from identifier (object)", () => {
const code = `
import { somePlugin1, somePlugin2 } from 'some-module'
const myConfig = {
plugins: [somePlugin1(), somePlugin2()]
};
export default myConfig;
`;

const mod = parseModule(code);

addVitePlugin(mod, {
index: 1,
from: "vite-plugin-pwa",
imported: "VitePWA",
constructor: "VitePWA",
});

expect(generate(mod)).toMatchInlineSnapshot(`
"import { VitePWA } from \\"vite-plugin-pwa\\";
import { somePlugin1, somePlugin2 } from \\"some-module\\";
const myConfig = {
plugins: [somePlugin1(), VitePWA(), somePlugin2()],
};
export default myConfig;"
`);
});
});

0 comments on commit 224eed2

Please sign in to comment.