Skip to content

Commit

Permalink
Merge pull request #47 from solidjs-community/fix/spa-vite-config
Browse files Browse the repository at this point in the history
Add vite config handling for SPAs
  • Loading branch information
Tommypop2 authored May 19, 2024
2 parents 0c4ffb3 + 36b0d4e commit e00dd59
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 46 deletions.
38 changes: 20 additions & 18 deletions packages/commands/src/handlers/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Integrations, Supported, integrations, setRootFile } from "../lib/integ
import * as p from "@clack/prompts";
import color from "picocolors";
import { primitives, loadPrimitives } from "@solid-cli/utils/primitives";
import { t } from "@solid-cli/utils";
import { fileExists, getRootFile, getAppConfig, validateFilePath } from "../lib/utils/helpers";
import { isSolidStart, t } from "@solid-cli/utils";
import { fileExists, getRootFile, getConfigFile, validateFilePath } from "../lib/utils/helpers";
import { writeFile, readFile } from "@solid-cli/utils/fs";
import { transformPlugins, type PluginOptions } from "@solid-cli/utils/transform";
import {
Expand Down Expand Up @@ -43,8 +43,9 @@ const handleAutocompleteAdd = async () => {
{ label: t.NO, value: false },
{ label: t.YES_FORCE, value: [true, "force"] },
],
message: `${t.CONFIRM_INSTALL(a.length)} \n${color.red(S_BAR)} \n${color.red(S_BAR)} ${" " + color.yellow(a.map((opt) => opt.label).join(" ")) + " "
} \n${color.red(S_BAR)} `,
message: `${t.CONFIRM_INSTALL(a.length)} \n${color.red(S_BAR)} \n${color.red(S_BAR)} ${
" " + color.yellow(a.map((opt) => opt.label).join(" ")) + " "
} \n${color.red(S_BAR)} `,
}),
);

Expand Down Expand Up @@ -109,7 +110,9 @@ export const handleAdd = async (packages?: string[], forceTransform: boolean = f
})
.filter((p) => p) as Configs;

const appConfig = await getAppConfig();
const configType = (await isSolidStart()) ? "app" : "vite";

const configFile = await getConfigFile(configType);

for (let i = 0; i < configs.length; i++) {
const config = configs[i];
Expand All @@ -124,20 +127,19 @@ export const handleAdd = async (packages?: string[], forceTransform: boolean = f
if (!configs.length) return;
const pluginOptions = configs.map((c) => c.pluginOptions).filter(Boolean) as PluginOptions[];
if (pluginOptions.length) {
const appConfig = await getAppConfig();
await spinnerify({
startText: "Processing config",
finishText: "Config processed",
fn: async () => {
const code = await transformPlugins(
configs.map((c) => c.pluginOptions).filter(Boolean) as PluginOptions[],
{ name: appConfig, contents: (await readFile(appConfig)).toString() },
forceTransform,
undefined,
);
await writeFile(appConfig, code);
},
});
startText: "Processing config",
finishText: "Config processed",
fn: async () => {
const code = await transformPlugins(
configs.map((c) => c.pluginOptions).filter(Boolean) as PluginOptions[],
{ type: configType, name: configFile, contents: (await readFile(configFile)).toString() },
forceTransform,
undefined,
);
await writeFile(configFile, code);
},
});
}

p.log.info("Preparing post install steps for integrations");
Expand Down
2 changes: 1 addition & 1 deletion packages/commands/src/handlers/start/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const handleAdapter = async (name?: string, forceTransform = false) => {
const sym = Symbol(name).toString();
let code = await transformPlugins(
[],
{ name: "app.config.ts", contents: (await readFile("app.config.ts")).toString() },
{ type: "app", name: "app.config.ts", contents: (await readFile("app.config.ts")).toString() },
forceTransform,
);
code = `import ${name} from "solid-start-${name}";\n` + code;
Expand Down
2 changes: 1 addition & 1 deletion packages/commands/src/handlers/start/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const handleMode = async (mode?: SupportedModes) => {
if (mode != "ssg") {
const newConfig = await transformPlugins(
[],
{ name: "app.config.ts", contents: (await readFile("app.config.ts")).toString() },
{ type: "app", name: "app.config.ts", contents: (await readFile("app.config.ts")).toString() },
true,
true,
);
Expand Down
16 changes: 8 additions & 8 deletions packages/commands/src/lib/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,30 +99,30 @@ export async function findFiles(
return filePaths;
}

export const getAppConfig = async () => {
let configFile = "app.config.ts";
export const getConfigFile = async (file: "app" | "vite" = "app") => {
let configFile = `${file}.config.ts`;

const existsHere = fileExists(configFile);

if (!existsHere) {
const root = await getProjectRoot();
const existsInRoot = validateFilePath(root, "app.config");
const existsInRoot = validateFilePath(root, `${file}.config`);
if (existsInRoot) {
const correctConfig = await cancelable(
p.confirm({
message: `Could not find app config in current directory, but found app config in \`${root}\`. Is this the correct vite config?`,
message: `Could not find ${file} config in current directory, but found ${file} config in \`${root}\`. Is this the correct ${file} config?`,
}),
);
if (correctConfig) return existsInRoot;
}

p.log.error(color.red(`Can't find app.config file`));
p.log.error(color.red(`Can't find ${file}.config file`));
await cancelable(
p.text({
message: "Type path to app config: ",
message: `Type path to ${file} config: `,
validate(value) {
const path = validateFilePath(value, "app.config");
if (!path) return `App config not found. Please try again`;
const path = validateFilePath(value, `${file}.config`);
if (!path) return `${file} config not found. Please try again`;
else {
configFile = path;
}
Expand Down
7 changes: 7 additions & 0 deletions packages/commands/tests/assets/sample_app_config.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from "@solidjs/start/config";
// Simulates an app config
export default defineConfig({
vite: {
plugins: [solid()]
}
});
9 changes: 9 additions & 0 deletions packages/commands/tests/assets/sample_unocss_vite_result.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import UnoCss from "unocss/vite";
import { defineConfig } from "vite";
// Simulates an app config
export default defineConfig({
plugins: [
solid(),
UnoCss({})
]
});
6 changes: 2 additions & 4 deletions packages/commands/tests/assets/sample_vite_config.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { defineConfig } from "@solidjs/start/config";
import { defineConfig } from "vite";
// Simulates an app config
export default defineConfig({
vite: {
plugins: [solid()]
}
plugins: [solid()]
});
52 changes: 39 additions & 13 deletions packages/commands/tests/config_manipulation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@ const readFile = async (path: string): Promise<string> => {
readFile1(path, (_, data) => res(data.toString())),
);
};

const removeWhitespace = (str: string) => str.replace(/\s/g, "");

vi.mock("fs/promises", () => {
return {
readFile: async (name: string) => {
if (name === "app.config.ts") {
const sampleConfig: string = await readFile("./packages/commands/tests/assets/sample_app_config.txt");
return sampleConfig;
}
if (name === "vite.config.ts") {
const sampleConfig: string = await readFile("./packages/commands/tests/assets/sample_vite_config.txt");
return sampleConfig;
}

return "{}";
},
writeFile: async (_, contents: string) => {
Expand Down Expand Up @@ -46,22 +53,41 @@ vi.mock("@solid-cli/utils/updates", async () => {

vi.mock("../src/lib/utils/helpers.ts", async () => {
return {
getAppConfig: async (): Promise<string> => new Promise((r) => r("app.config.ts")),
fileExists: (path: string) => path.includes("app_config") || path.includes("app.tsx") || path.includes("index.tsx"),
getConfigFile: async (file: string): Promise<string> => new Promise((r) => r(`${file}.config.ts`)),
fileExists: (path: string) =>
path.includes("vite_config") ||
path.includes("app_config") ||
path.includes("app.tsx") ||
path.includes("index.tsx"),
getRootFile: async (): Promise<string> => new Promise((r) => r("./src/app.tsx")),
};
});

let testingSolidStart = false;

vi.mock("@solid-cli/utils", async () => {
return {
isSolidStart: async () => new Promise((r) => r(testingSolidStart)),
};
});

describe("Update config", () => {
it(
"Adds a plugin properly to the config",
async () => {
await handleAdd(["unocss"]);
it("Adds a plugin properly to the app config", { timeout: 50000 }, async () => {
testingSolidStart = true;
await handleAdd(["unocss"]);

const expected = await readFile("./packages/commands/tests/assets/sample_unocss_result.txt");
// @ts-ignore
const newConfig = UPDATESQUEUE.find((u) => u.name === "app.config.ts")?.contents;
expect(removeWhitespace(expected)).toBe(removeWhitespace(newConfig));
},
{ timeout: 50000 },
);
const expected = await readFile("./packages/commands/tests/assets/sample_unocss_app_result.txt");
// @ts-ignore
const newConfig = UPDATESQUEUE.find((u) => u.name === "app.config.ts")?.contents;
expect(removeWhitespace(newConfig)).toBe(removeWhitespace(expected));
});
it("Adds a plugin properly to the vite config", { timeout: 50000 }, async () => {
testingSolidStart = false;
await handleAdd(["unocss"]);

const expected = await readFile("./packages/commands/tests/assets/sample_unocss_vite_result.txt");
// @ts-ignore
const newConfig = UPDATESQUEUE.find((u) => u.name === "vite.config.ts")?.contents;
expect(removeWhitespace(newConfig)).toBe(removeWhitespace(expected));
});
});
18 changes: 17 additions & 1 deletion packages/utils/src/transform/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { parseModule } from "magicast";
import { generateCode, parseModule } from "magicast";
import { addVitePlugin } from "magicast/helpers";
import { addPlugins } from "./parse";

export type Config = {
type: "app" | "vite";
name: string;
contents: string;
};
Expand All @@ -15,6 +17,20 @@ export const transformPlugins = async (
_merge_configs = false,
) => {
const mod = parseModule(config.contents, { trailingComma: false, flowObjectCommas: false });

if (config.type === "vite") {
for (const plugin of new_plugins) {
addVitePlugin(mod, {
imported: plugin.isDefault ? "default" : plugin.importName,
from: plugin.importSource,
constructor: plugin.importName,
options: plugin.options,
});
}

return generateCode(mod).code;
}

return addPlugins(mod, new_plugins).code;
};
// All the integrations/packages that we support
Expand Down
31 changes: 31 additions & 0 deletions packages/utils/tests/plugin_transforms.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,36 @@ const examplePlugin: PluginOptions = {
options: {},
};
describe("transformPlugins", () => {
test("SPA config is updated properly", async () => {
const config: Config = {
type: "vite",
name: "vite.config.ts",
contents: `
import { defineConfig } from "vite";
// Simulates a vite config
export default defineConfig({
plugins: []
});
`,
};
const expected = `
import examplePlugin from "example";
import { defineConfig } from "vite";
// Simulates a vite config
export default defineConfig({
plugins: [examplePlugin({})]
});
`;

const result = await transformPlugins([examplePlugin], config);

expect(removeWhitespace(result)).toBe(removeWhitespace(expected));
});
test("No vite property defined config is updated properly", async () => {
const config: Config = {
type: "app",
name: "app.config.ts",
contents: `
import { defineConfig } from "@solidjs/start/config";
Expand All @@ -67,6 +95,7 @@ describe("transformPlugins", () => {
});
test("Object config is updated properly", async () => {
const config: Config = {
type: "app",
name: "app.config.ts",
contents: makeExampleConfig(["solid()"]),
};
Expand All @@ -77,6 +106,7 @@ describe("transformPlugins", () => {
});
test("Arrow-function config is updated properly", async () => {
const config: Config = {
type: "app",
name: "app.config.ts",
contents: makeExampleConfig(["solid()"], [], "arrow"),
};
Expand All @@ -92,6 +122,7 @@ describe("transformPlugins", () => {

test("Object method config is updated properly", async () => {
const config: Config = {
type: "app",
name: "app.config.ts",
contents: makeExampleConfig(["solid()"], [], "method"),
};
Expand Down

0 comments on commit e00dd59

Please sign in to comment.