@mochi-css/tsuki 2.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/index.js +249 -151
- package/package.json +6 -11
package/README.md
CHANGED
|
@@ -8,3 +8,22 @@ You can run it with:
|
|
|
8
8
|
```bash
|
|
9
9
|
npx @mochi-css/tsuki
|
|
10
10
|
```
|
|
11
|
+
|
|
12
|
+
`tsuki` handles installing integrations for next.js and vite frameworks.
|
|
13
|
+
To get more info, run it with `-h` or `--help` option.
|
|
14
|
+
|
|
15
|
+
## What `tsuki` sets up
|
|
16
|
+
|
|
17
|
+
During initialization, `tsuki` installs the required packages and creates a `mochi.config.ts` file in your project root.
|
|
18
|
+
This file is the single place to configure all Mochi-CSS options (`roots`, `plugins`, `splitCss`, etc.) - all integrations load it automatically.
|
|
19
|
+
|
|
20
|
+
## Presets
|
|
21
|
+
|
|
22
|
+
Use the `--preset` / `-p` flag to choose a framework preset non-interactively:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx @mochi-css/tsuki --preset vite
|
|
26
|
+
npx @mochi-css/tsuki --preset nextjs
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If `-p` is omitted, `tsuki` will prompt you to select a preset interactively.
|
package/dist/index.js
CHANGED
|
@@ -1,49 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
-
value: mod,
|
|
21
|
-
enumerable: true
|
|
22
|
-
}) : target, mod));
|
|
23
|
-
|
|
24
|
-
//#endregion
|
|
25
|
-
let commander = require("commander");
|
|
26
|
-
commander = __toESM(commander);
|
|
27
|
-
let __clack_prompts = require("@clack/prompts");
|
|
28
|
-
__clack_prompts = __toESM(__clack_prompts);
|
|
29
|
-
let picocolors = require("picocolors");
|
|
30
|
-
picocolors = __toESM(picocolors);
|
|
31
|
-
let node_readline = require("node:readline");
|
|
32
|
-
node_readline = __toESM(node_readline);
|
|
33
|
-
let node_stream = require("node:stream");
|
|
34
|
-
node_stream = __toESM(node_stream);
|
|
35
|
-
let package_manager_detector = require("package-manager-detector");
|
|
36
|
-
package_manager_detector = __toESM(package_manager_detector);
|
|
37
|
-
let cross_spawn = require("cross-spawn");
|
|
38
|
-
cross_spawn = __toESM(cross_spawn);
|
|
39
|
-
let fs_extra = require("fs-extra");
|
|
40
|
-
fs_extra = __toESM(fs_extra);
|
|
41
|
-
let fs_promises = require("fs/promises");
|
|
42
|
-
fs_promises = __toESM(fs_promises);
|
|
43
|
-
let path = require("path");
|
|
44
|
-
path = __toESM(path);
|
|
45
|
-
let magicast = require("magicast");
|
|
46
|
-
magicast = __toESM(magicast);
|
|
2
|
+
import { Option, program } from "commander";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
6
|
+
import { PassThrough } from "node:stream";
|
|
7
|
+
import { detect, resolveCommand } from "package-manager-detector";
|
|
8
|
+
import spawn from "cross-spawn";
|
|
9
|
+
import fsExtra from "fs-extra";
|
|
10
|
+
import fs from "fs/promises";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import { generateCode, parseModule } from "magicast";
|
|
13
|
+
import dedent from "dedent";
|
|
47
14
|
|
|
48
15
|
//#region src/install.ts
|
|
49
16
|
function onceEvent(emitter, event) {
|
|
@@ -55,7 +22,7 @@ function onceEvent(emitter, event) {
|
|
|
55
22
|
}
|
|
56
23
|
function spawnProcess(command, args, options) {
|
|
57
24
|
return new Promise((resolve, reject) => {
|
|
58
|
-
const child = (
|
|
25
|
+
const child = spawn(command, args, options);
|
|
59
26
|
const onSpawn = () => {
|
|
60
27
|
child.off("error", onError);
|
|
61
28
|
resolve(child);
|
|
@@ -69,7 +36,7 @@ function spawnProcess(command, args, options) {
|
|
|
69
36
|
});
|
|
70
37
|
}
|
|
71
38
|
async function runInstall(title, command, args, packages) {
|
|
72
|
-
const log =
|
|
39
|
+
const log = p.taskLog({ title });
|
|
73
40
|
log.message(`${command} ${args.join(" ")}`);
|
|
74
41
|
try {
|
|
75
42
|
const child = await spawnProcess(command, args, { stdio: [
|
|
@@ -77,7 +44,7 @@ async function runInstall(title, command, args, packages) {
|
|
|
77
44
|
"pipe",
|
|
78
45
|
"pipe"
|
|
79
46
|
] });
|
|
80
|
-
const merged = new
|
|
47
|
+
const merged = new PassThrough();
|
|
81
48
|
let openStreams = 0;
|
|
82
49
|
for (const stream of [child.stdout, child.stderr]) {
|
|
83
50
|
if (!stream) continue;
|
|
@@ -87,7 +54,7 @@ async function runInstall(title, command, args, packages) {
|
|
|
87
54
|
if (--openStreams === 0) merged.end();
|
|
88
55
|
});
|
|
89
56
|
}
|
|
90
|
-
const rl =
|
|
57
|
+
const rl = createInterface({
|
|
91
58
|
input: merged,
|
|
92
59
|
crlfDelay: Infinity
|
|
93
60
|
});
|
|
@@ -105,7 +72,7 @@ async function runInstall(title, command, args, packages) {
|
|
|
105
72
|
}
|
|
106
73
|
async function installPackages(packages, autoInstall = false) {
|
|
107
74
|
if (packages.length === 0) return;
|
|
108
|
-
const packageManager = await
|
|
75
|
+
const packageManager = await detect({ strategies: [
|
|
109
76
|
"packageManager-field",
|
|
110
77
|
"devEngines-field",
|
|
111
78
|
"lockfile",
|
|
@@ -118,19 +85,19 @@ async function installPackages(packages, autoInstall = false) {
|
|
|
118
85
|
const prodPackages = packages.filter((pkg) => pkg.dev === false).map((pkg) => pkg.name);
|
|
119
86
|
const packageList = [...devPackages.map((name) => `${name} (dev)`), ...prodPackages.map((name) => name)].join(", ");
|
|
120
87
|
if (!autoInstall) {
|
|
121
|
-
const confirmed = await
|
|
122
|
-
if (
|
|
123
|
-
|
|
88
|
+
const confirmed = await p.confirm({ message: `Install the following packages: ${packageList}?` });
|
|
89
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
90
|
+
p.log.info("Skipping package installation");
|
|
124
91
|
return;
|
|
125
92
|
}
|
|
126
93
|
}
|
|
127
94
|
if (devPackages.length > 0) {
|
|
128
|
-
const cmd =
|
|
95
|
+
const cmd = resolveCommand(agent, "add", [devFlag, ...devPackages]);
|
|
129
96
|
if (cmd === null) throw new Error("Could not prepare install command");
|
|
130
97
|
await runInstall(`Installing dev dependencies: ${devPackages.join(", ")}`, cmd.command, cmd.args, devPackages);
|
|
131
98
|
}
|
|
132
99
|
if (prodPackages.length > 0) {
|
|
133
|
-
const cmd =
|
|
100
|
+
const cmd = resolveCommand(agent, "add", prodPackages);
|
|
134
101
|
if (cmd === null) throw new Error("Could not prepare install command");
|
|
135
102
|
await runInstall(`Installing dependencies: ${prodPackages.join(", ")}`, cmd.command, cmd.args, prodPackages);
|
|
136
103
|
}
|
|
@@ -141,8 +108,8 @@ async function installPackages(packages, autoInstall = false) {
|
|
|
141
108
|
var ModuleRunner = class {
|
|
142
109
|
packages = [];
|
|
143
110
|
modules = [];
|
|
144
|
-
register(module
|
|
145
|
-
this.modules.push(module
|
|
111
|
+
register(module) {
|
|
112
|
+
this.modules.push(module);
|
|
146
113
|
return this;
|
|
147
114
|
}
|
|
148
115
|
async run(options = {}) {
|
|
@@ -163,11 +130,17 @@ var ModuleRunner = class {
|
|
|
163
130
|
nonInteractive,
|
|
164
131
|
moduleOptions
|
|
165
132
|
};
|
|
166
|
-
for (const module
|
|
133
|
+
for (const module of this.modules) await module.run(ctx);
|
|
167
134
|
if (this.packages.length > 0) await installPackages(this.packages, autoInstall);
|
|
168
135
|
}
|
|
169
136
|
};
|
|
170
137
|
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/version.ts
|
|
140
|
+
function mochiPackage(name) {
|
|
141
|
+
return `${name}@^${parseInt("4.0.0".split(".")[0] ?? "0", 10)}.0.0`;
|
|
142
|
+
}
|
|
143
|
+
|
|
171
144
|
//#endregion
|
|
172
145
|
//#region src/modules/ast.ts
|
|
173
146
|
function getPropKeyName(prop) {
|
|
@@ -175,6 +148,29 @@ function getPropKeyName(prop) {
|
|
|
175
148
|
if (typeof key["name"] === "string") return key["name"];
|
|
176
149
|
if (typeof key["value"] === "string") return key["value"];
|
|
177
150
|
}
|
|
151
|
+
function getPluginsElements(obj, configPath) {
|
|
152
|
+
const existing = obj.properties.find((prop) => getPropKeyName(prop) === "plugins");
|
|
153
|
+
if (existing) {
|
|
154
|
+
const value = existing["value"];
|
|
155
|
+
if (value["type"] !== "ArrayExpression") throw new Error(`Unrecognized plugins config type in ${configPath}`);
|
|
156
|
+
return value["elements"];
|
|
157
|
+
}
|
|
158
|
+
const elements = [];
|
|
159
|
+
obj.properties.push({
|
|
160
|
+
type: "ObjectProperty",
|
|
161
|
+
key: {
|
|
162
|
+
type: "Identifier",
|
|
163
|
+
name: "plugins"
|
|
164
|
+
},
|
|
165
|
+
value: {
|
|
166
|
+
type: "ArrayExpression",
|
|
167
|
+
elements
|
|
168
|
+
},
|
|
169
|
+
computed: false,
|
|
170
|
+
shorthand: false
|
|
171
|
+
});
|
|
172
|
+
return elements;
|
|
173
|
+
}
|
|
178
174
|
function optionsToAstProperties(options) {
|
|
179
175
|
return Object.entries(options).map(([key, value]) => ({
|
|
180
176
|
type: "ObjectProperty",
|
|
@@ -200,13 +196,9 @@ function optionsToAstProperties(options) {
|
|
|
200
196
|
//#endregion
|
|
201
197
|
//#region src/modules/postcss.ts
|
|
202
198
|
const postcssConfigNames = [
|
|
203
|
-
"postcss.config.mts",
|
|
204
|
-
"postcss.config.ts",
|
|
205
199
|
"postcss.config.mjs",
|
|
206
200
|
"postcss.config.js",
|
|
207
201
|
"postcss.config.cjs",
|
|
208
|
-
".postcssrc.mts",
|
|
209
|
-
".postcssrc.ts",
|
|
210
202
|
".postcssrc.mjs",
|
|
211
203
|
".postcssrc.js",
|
|
212
204
|
".postcssrc.cjs",
|
|
@@ -216,20 +208,20 @@ const postcssConfigNames = [
|
|
|
216
208
|
".postcssrc"
|
|
217
209
|
];
|
|
218
210
|
function findPostcssConfig() {
|
|
219
|
-
return postcssConfigNames.find((name) =>
|
|
211
|
+
return postcssConfigNames.find((name) => fsExtra.existsSync(name));
|
|
220
212
|
}
|
|
221
213
|
function defaultPostcssConfig(pluginOptions = {}) {
|
|
222
214
|
const entries = Object.entries(pluginOptions);
|
|
223
215
|
return `export default {\n plugins: {\n "@mochi-css/postcss": ${entries.length === 0 ? "{}" : `{\n${entries.map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`).join(",\n")}\n }`}\n }\n}\n`;
|
|
224
216
|
}
|
|
225
217
|
async function askForPath() {
|
|
226
|
-
const defaultConfig = findPostcssConfig() ?? "postcss.config.
|
|
227
|
-
const configPath = await
|
|
218
|
+
const defaultConfig = findPostcssConfig() ?? "postcss.config.mjs";
|
|
219
|
+
const configPath = await p.text({
|
|
228
220
|
message: "Path to PostCSS config",
|
|
229
221
|
placeholder: defaultConfig,
|
|
230
222
|
defaultValue: defaultConfig
|
|
231
223
|
});
|
|
232
|
-
if (
|
|
224
|
+
if (p.isCancel(configPath)) return false;
|
|
233
225
|
return configPath;
|
|
234
226
|
}
|
|
235
227
|
function isProxy(v) {
|
|
@@ -248,6 +240,7 @@ function addPluginToNamedVar(mod, varName, pluginName, pluginOptions, configPath
|
|
|
248
240
|
if (!init) throw new Error(`Failed to add postcss plugin to ${configPath}`);
|
|
249
241
|
const pluginsProp = init.properties.find((prop) => getPropKeyName(prop) === "plugins");
|
|
250
242
|
if (!pluginsProp) throw new Error(`Failed to find plugins object in ${configPath}`);
|
|
243
|
+
if (pluginsProp.value.properties.some((prop) => getPropKeyName(prop) === pluginName)) return;
|
|
251
244
|
pluginsProp.value.properties.push({
|
|
252
245
|
type: "ObjectProperty",
|
|
253
246
|
key: {
|
|
@@ -267,12 +260,12 @@ function addPluginToNamedVar(mod, varName, pluginName, pluginOptions, configPath
|
|
|
267
260
|
throw new Error(`Failed to add postcss plugin to ${configPath}`);
|
|
268
261
|
}
|
|
269
262
|
async function addPostcssPlugin(configPath, pluginName, pluginOptions = {}) {
|
|
270
|
-
const mod =
|
|
263
|
+
const mod = parseModule(await fs.readFile(configPath, "utf-8"));
|
|
271
264
|
const defaultExport = mod.exports["default"];
|
|
272
265
|
if (isProxy(defaultExport) && defaultExport.$type === "identifier") {
|
|
273
266
|
addPluginToNamedVar(mod, defaultExport.$name, pluginName, pluginOptions, configPath);
|
|
274
|
-
const { code: code$1 } =
|
|
275
|
-
await
|
|
267
|
+
const { code: code$1 } = generateCode(mod);
|
|
268
|
+
await fs.writeFile(configPath, code$1);
|
|
276
269
|
return;
|
|
277
270
|
}
|
|
278
271
|
const config = isProxy(defaultExport) && defaultExport.$type === "function-call" ? defaultExport.$args[0] : defaultExport;
|
|
@@ -280,30 +273,28 @@ async function addPostcssPlugin(configPath, pluginName, pluginOptions = {}) {
|
|
|
280
273
|
if ("plugins" in config && config["plugins"] !== void 0 && !isObject(config["plugins"])) throw new Error(`Unrecognized plugins config type in ${configPath}`);
|
|
281
274
|
config["plugins"] ??= {};
|
|
282
275
|
config["plugins"][pluginName] = pluginOptions;
|
|
283
|
-
const { code } =
|
|
284
|
-
await
|
|
276
|
+
const { code } = generateCode(mod);
|
|
277
|
+
await fs.writeFile(configPath, code);
|
|
285
278
|
}
|
|
286
279
|
async function addToConfig(configPath, pluginOptions = {}) {
|
|
287
|
-
if (!
|
|
288
|
-
await
|
|
280
|
+
if (!fsExtra.existsSync(configPath)) {
|
|
281
|
+
await fsExtra.writeFile("postcss.config.mjs", defaultPostcssConfig(pluginOptions));
|
|
289
282
|
return;
|
|
290
283
|
}
|
|
291
284
|
const ext = configPath.split(".").pop();
|
|
292
|
-
if (ext === "json" || path.
|
|
293
|
-
const config = await
|
|
285
|
+
if (ext === "json" || path.basename(configPath) === ".postcssrc") {
|
|
286
|
+
const config = await fsExtra.readJson(configPath);
|
|
294
287
|
if (!isObject(config)) throw new Error("Unrecognized config type in ${configPath}`)");
|
|
295
288
|
config["plugins"] ??= {};
|
|
296
289
|
if (!isObject(config["plugins"])) throw new Error("Unrecognized config type in ${configPath}`)");
|
|
297
290
|
config["plugins"]["@mochi-css/postcss"] = pluginOptions;
|
|
298
|
-
await
|
|
291
|
+
await fsExtra.writeJson(configPath, config, { spaces: 2 });
|
|
299
292
|
return;
|
|
300
293
|
}
|
|
301
294
|
if (ext === "yml" || ext === "yaml") throw new Error("YAML PostCSS config is not supported yet");
|
|
302
295
|
await addPostcssPlugin(configPath, "@mochi-css/postcss", pluginOptions);
|
|
303
296
|
}
|
|
304
297
|
function createPostcssModule(options = {}) {
|
|
305
|
-
const pluginOptions = {};
|
|
306
|
-
if (options.outDir !== void 0) pluginOptions["outDir"] = options.outDir;
|
|
307
298
|
return {
|
|
308
299
|
id: "postcss",
|
|
309
300
|
name: "PostCSS",
|
|
@@ -314,15 +305,15 @@ function createPostcssModule(options = {}) {
|
|
|
314
305
|
else if (options.auto) configPath = findPostcssConfig() ?? "postcss.config.mts";
|
|
315
306
|
else if (ctx.nonInteractive) return;
|
|
316
307
|
else {
|
|
317
|
-
const usePostcss = await
|
|
318
|
-
if (
|
|
308
|
+
const usePostcss = await p.confirm({ message: "Do you use PostCSS?" });
|
|
309
|
+
if (p.isCancel(usePostcss) || !usePostcss) return;
|
|
319
310
|
const selected = await askForPath();
|
|
320
311
|
if (selected === false) return;
|
|
321
312
|
configPath = selected;
|
|
322
313
|
}
|
|
323
|
-
await addToConfig(configPath
|
|
324
|
-
|
|
325
|
-
ctx.requirePackage("@mochi-css/postcss");
|
|
314
|
+
await addToConfig(configPath);
|
|
315
|
+
p.log.success("Added mochi plugin to the postcss config");
|
|
316
|
+
ctx.requirePackage(mochiPackage("@mochi-css/postcss"));
|
|
326
317
|
}
|
|
327
318
|
};
|
|
328
319
|
}
|
|
@@ -334,7 +325,7 @@ const libPreset = {
|
|
|
334
325
|
id: "lib",
|
|
335
326
|
name: "Library",
|
|
336
327
|
setup(runner) {
|
|
337
|
-
|
|
328
|
+
p.log.warn("Library preset is not fully supported yet.");
|
|
338
329
|
runner.register(createPostcssModule());
|
|
339
330
|
}
|
|
340
331
|
};
|
|
@@ -348,38 +339,16 @@ const viteConfigNames = [
|
|
|
348
339
|
"vite.config.mjs"
|
|
349
340
|
];
|
|
350
341
|
function findViteConfig() {
|
|
351
|
-
return viteConfigNames.find((name) =>
|
|
342
|
+
return viteConfigNames.find((name) => fsExtra.existsSync(name));
|
|
352
343
|
}
|
|
353
|
-
const defaultViteConfig = `
|
|
354
|
-
import {
|
|
344
|
+
const defaultViteConfig = dedent`
|
|
345
|
+
import { defineConfig } from "vite"
|
|
346
|
+
import { mochiCss } from "@mochi-css/vite"
|
|
355
347
|
|
|
356
|
-
export default defineConfig({
|
|
357
|
-
|
|
358
|
-
})
|
|
348
|
+
export default defineConfig({
|
|
349
|
+
plugins: [mochiCss()],
|
|
350
|
+
})
|
|
359
351
|
`;
|
|
360
|
-
function getPluginsElements(obj, configPath) {
|
|
361
|
-
const existing = obj.properties.find((prop) => getPropKeyName(prop) === "plugins");
|
|
362
|
-
if (existing) {
|
|
363
|
-
const value = existing["value"];
|
|
364
|
-
if (value["type"] !== "ArrayExpression") throw new Error(`Unrecognized plugins config type in ${configPath}`);
|
|
365
|
-
return value["elements"];
|
|
366
|
-
}
|
|
367
|
-
const elements = [];
|
|
368
|
-
obj.properties.push({
|
|
369
|
-
type: "ObjectProperty",
|
|
370
|
-
key: {
|
|
371
|
-
type: "Identifier",
|
|
372
|
-
name: "plugins"
|
|
373
|
-
},
|
|
374
|
-
value: {
|
|
375
|
-
type: "ArrayExpression",
|
|
376
|
-
elements
|
|
377
|
-
},
|
|
378
|
-
computed: false,
|
|
379
|
-
shorthand: false
|
|
380
|
-
});
|
|
381
|
-
return elements;
|
|
382
|
-
}
|
|
383
352
|
function addPluginCallToObj(obj, configPath) {
|
|
384
353
|
getPluginsElements(obj, configPath).push({
|
|
385
354
|
type: "CallExpression",
|
|
@@ -421,15 +390,15 @@ function addToVitePlugins(mod, configPath) {
|
|
|
421
390
|
throw new Error(`Failed to add vite plugin to ${configPath}`);
|
|
422
391
|
}
|
|
423
392
|
async function addMochiToViteConfig(configPath) {
|
|
424
|
-
const mod =
|
|
393
|
+
const mod = parseModule(await fs.readFile(configPath, "utf-8"));
|
|
425
394
|
mod.imports.$prepend({
|
|
426
395
|
from: "@mochi-css/vite",
|
|
427
396
|
imported: "mochiCss",
|
|
428
397
|
local: "mochiCss"
|
|
429
398
|
});
|
|
430
399
|
addToVitePlugins(mod, configPath);
|
|
431
|
-
const { code } =
|
|
432
|
-
await
|
|
400
|
+
const { code } = generateCode(mod);
|
|
401
|
+
await fs.writeFile(configPath, code);
|
|
433
402
|
}
|
|
434
403
|
const viteModule = {
|
|
435
404
|
id: "vite",
|
|
@@ -442,33 +411,157 @@ const viteModule = {
|
|
|
442
411
|
else if (existingConfig) configPath = existingConfig;
|
|
443
412
|
else if (ctx.nonInteractive) configPath = "vite.config.ts";
|
|
444
413
|
else {
|
|
445
|
-
const selected = await
|
|
414
|
+
const selected = await p.text({
|
|
446
415
|
message: "Path to Vite config",
|
|
447
416
|
placeholder: "vite.config.ts",
|
|
448
417
|
defaultValue: "vite.config.ts"
|
|
449
418
|
});
|
|
450
|
-
if (
|
|
419
|
+
if (p.isCancel(selected)) return;
|
|
451
420
|
configPath = selected;
|
|
452
421
|
}
|
|
453
|
-
if (!
|
|
454
|
-
await
|
|
455
|
-
|
|
422
|
+
if (!fsExtra.existsSync(configPath)) {
|
|
423
|
+
await fs.writeFile(configPath, defaultViteConfig);
|
|
424
|
+
p.log.success("Created vite config with mochi plugin");
|
|
456
425
|
} else {
|
|
457
426
|
await addMochiToViteConfig(configPath);
|
|
458
|
-
|
|
427
|
+
p.log.success("Added mochiCss() to vite config");
|
|
459
428
|
}
|
|
460
|
-
ctx.requirePackage("@mochi-css/vite");
|
|
429
|
+
ctx.requirePackage(mochiPackage("@mochi-css/vite"));
|
|
461
430
|
}
|
|
462
431
|
};
|
|
463
432
|
|
|
433
|
+
//#endregion
|
|
434
|
+
//#region src/modules/mochiConfig.ts
|
|
435
|
+
const mochiConfigNames = [
|
|
436
|
+
"mochi.config.ts",
|
|
437
|
+
"mochi.config.mts",
|
|
438
|
+
"mochi.config.js",
|
|
439
|
+
"mochi.config.mjs"
|
|
440
|
+
];
|
|
441
|
+
function findMochiConfig() {
|
|
442
|
+
return mochiConfigNames.find((name) => fsExtra.existsSync(name));
|
|
443
|
+
}
|
|
444
|
+
const defaultMochiConfigBase = dedent`
|
|
445
|
+
import { defineConfig } from "@mochi-css/vanilla/config"
|
|
446
|
+
|
|
447
|
+
export default defineConfig({})
|
|
448
|
+
`;
|
|
449
|
+
function defaultMochiConfigWithOptions(tmpDir, styledId) {
|
|
450
|
+
const importPath = styledId ? "@mochi-css/vanilla-react/config" : "@mochi-css/vanilla/config";
|
|
451
|
+
const lines = [];
|
|
452
|
+
if (tmpDir !== void 0) lines.push(` tmpDir: ${JSON.stringify(tmpDir)},`);
|
|
453
|
+
if (!styledId && lines.length === 0) return defaultMochiConfigBase;
|
|
454
|
+
return `import { defineConfig } from "${importPath}"\n\nexport default defineConfig({\n${lines.join("\n")}\n})\n`;
|
|
455
|
+
}
|
|
456
|
+
function getConfigObject(mod) {
|
|
457
|
+
const exportDefault = mod.$ast.body.find((s) => s.type === "ExportDefaultDeclaration");
|
|
458
|
+
if (!exportDefault) return void 0;
|
|
459
|
+
const decl = exportDefault.declaration;
|
|
460
|
+
if (decl["type"] === "ObjectExpression") return decl;
|
|
461
|
+
if (decl["type"] === "CallExpression") {
|
|
462
|
+
const firstArg = decl["arguments"][0];
|
|
463
|
+
if (firstArg?.["type"] === "ObjectExpression") return firstArg;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
async function addTmpDirToExistingConfig(configPath, tmpDir) {
|
|
467
|
+
const mod = parseModule(await fs.readFile(configPath, "utf-8"));
|
|
468
|
+
const obj = getConfigObject(mod);
|
|
469
|
+
if (!obj) throw new Error(`Failed to add tmpDir to ${configPath}`);
|
|
470
|
+
if (obj["properties"].some((prop) => getPropKeyName(prop) === "tmpDir")) return;
|
|
471
|
+
obj["properties"] = [{
|
|
472
|
+
type: "ObjectProperty",
|
|
473
|
+
key: {
|
|
474
|
+
type: "StringLiteral",
|
|
475
|
+
value: "tmpDir"
|
|
476
|
+
},
|
|
477
|
+
value: {
|
|
478
|
+
type: "StringLiteral",
|
|
479
|
+
value: tmpDir
|
|
480
|
+
},
|
|
481
|
+
computed: false,
|
|
482
|
+
shorthand: false
|
|
483
|
+
}, ...obj["properties"]];
|
|
484
|
+
const { code } = generateCode(mod);
|
|
485
|
+
await fs.writeFile(configPath, code);
|
|
486
|
+
}
|
|
487
|
+
async function patchToVanillaReact(configPath) {
|
|
488
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
489
|
+
if (content.includes("@mochi-css/vanilla-react")) return;
|
|
490
|
+
const patched = content.replace(/"@mochi-css\/vanilla\/config"/g, "\"@mochi-css/vanilla-react/config\"");
|
|
491
|
+
await fs.writeFile(configPath, patched);
|
|
492
|
+
}
|
|
493
|
+
function createMochiConfigModule(options = {}) {
|
|
494
|
+
const { styledId = false, tmpDir } = options;
|
|
495
|
+
return {
|
|
496
|
+
id: "mochi-config",
|
|
497
|
+
name: "Mochi Config",
|
|
498
|
+
async run(ctx) {
|
|
499
|
+
const existing = findMochiConfig();
|
|
500
|
+
if (!existing) {
|
|
501
|
+
await fs.writeFile("mochi.config.ts", defaultMochiConfigWithOptions(tmpDir, styledId));
|
|
502
|
+
p.log.success("Created mochi.config.ts");
|
|
503
|
+
} else {
|
|
504
|
+
if (tmpDir !== void 0) try {
|
|
505
|
+
await addTmpDirToExistingConfig(existing, tmpDir);
|
|
506
|
+
p.log.success(`Added tmpDir to ${existing}`);
|
|
507
|
+
} catch {
|
|
508
|
+
p.log.warn(`Could not automatically add tmpDir to ${existing} — add it manually`);
|
|
509
|
+
}
|
|
510
|
+
if (styledId) try {
|
|
511
|
+
await patchToVanillaReact(existing);
|
|
512
|
+
p.log.success("Switched mochi.config to @mochi-css/vanilla-react/config");
|
|
513
|
+
} catch {
|
|
514
|
+
p.log.warn(`Could not patch ${existing} — change the import to @mochi-css/vanilla-react/config manually`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
ctx.requirePackage(mochiPackage("@mochi-css/vanilla"));
|
|
518
|
+
if (styledId) ctx.requirePackage(mochiPackage("@mochi-css/vanilla-react"));
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/modules/uiFramework.ts
|
|
525
|
+
async function patchConfigForReact(configPath) {
|
|
526
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
527
|
+
if (content.includes("@mochi-css/vanilla-react")) return;
|
|
528
|
+
const patched = content.replace(/"@mochi-css\/vanilla\/config"/g, "\"@mochi-css/vanilla-react/config\"");
|
|
529
|
+
await fs.writeFile(configPath, patched);
|
|
530
|
+
}
|
|
531
|
+
function createUiFrameworkModule(options = {}) {
|
|
532
|
+
return {
|
|
533
|
+
id: "ui-framework",
|
|
534
|
+
name: "UI Framework",
|
|
535
|
+
async run(ctx) {
|
|
536
|
+
const { framework: cliOption } = ctx.moduleOptions;
|
|
537
|
+
let useReact = options.auto === true || cliOption === "react";
|
|
538
|
+
if (!useReact) {
|
|
539
|
+
if (ctx.nonInteractive) return;
|
|
540
|
+
const confirmed = await p.confirm({ message: "Do you use React?" });
|
|
541
|
+
if (p.isCancel(confirmed) || !confirmed) return;
|
|
542
|
+
useReact = true;
|
|
543
|
+
}
|
|
544
|
+
ctx.requirePackage(mochiPackage("@mochi-css/vanilla-react"), false);
|
|
545
|
+
const configPath = findMochiConfig();
|
|
546
|
+
if (configPath) try {
|
|
547
|
+
await patchConfigForReact(configPath);
|
|
548
|
+
} catch {
|
|
549
|
+
p.log.warn(`Could not patch ${configPath} — change the import to @mochi-css/vanilla-react/config manually`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
464
555
|
//#endregion
|
|
465
556
|
//#region src/presets/vite.ts
|
|
466
557
|
const vitePreset = {
|
|
467
558
|
id: "vite",
|
|
468
559
|
name: "Vite",
|
|
469
560
|
setup(runner) {
|
|
470
|
-
runner.register(
|
|
561
|
+
runner.register(createMochiConfigModule({ tmpDir: ".mochi" }));
|
|
562
|
+
runner.register(createPostcssModule());
|
|
471
563
|
runner.register(viteModule);
|
|
564
|
+
runner.register(createUiFrameworkModule());
|
|
472
565
|
}
|
|
473
566
|
};
|
|
474
567
|
|
|
@@ -481,11 +574,12 @@ const nextConfigNames = [
|
|
|
481
574
|
"next.config.mjs"
|
|
482
575
|
];
|
|
483
576
|
function findNextConfig() {
|
|
484
|
-
return nextConfigNames.find((name) =>
|
|
577
|
+
return nextConfigNames.find((name) => fsExtra.existsSync(name));
|
|
485
578
|
}
|
|
486
|
-
const defaultNextConfig = `
|
|
579
|
+
const defaultNextConfig = dedent`
|
|
580
|
+
import { withMochi } from "@mochi-css/next"
|
|
487
581
|
|
|
488
|
-
export default withMochi({})
|
|
582
|
+
export default withMochi({})
|
|
489
583
|
`;
|
|
490
584
|
function wrapExportDefault(mod, configPath) {
|
|
491
585
|
const exportDefault = mod.$ast.body.find((s) => s.type === "ExportDefaultDeclaration");
|
|
@@ -500,15 +594,15 @@ function wrapExportDefault(mod, configPath) {
|
|
|
500
594
|
};
|
|
501
595
|
}
|
|
502
596
|
async function addMochiToNextConfig(configPath) {
|
|
503
|
-
const mod =
|
|
597
|
+
const mod = parseModule(await fs.readFile(configPath, "utf-8"));
|
|
504
598
|
mod.imports.$prepend({
|
|
505
599
|
from: "@mochi-css/next",
|
|
506
600
|
imported: "withMochi",
|
|
507
601
|
local: "withMochi"
|
|
508
602
|
});
|
|
509
603
|
wrapExportDefault(mod, configPath);
|
|
510
|
-
const { code } =
|
|
511
|
-
await
|
|
604
|
+
const { code } = generateCode(mod);
|
|
605
|
+
await fs.writeFile(configPath, code);
|
|
512
606
|
}
|
|
513
607
|
const nextModule = {
|
|
514
608
|
id: "next",
|
|
@@ -521,22 +615,22 @@ const nextModule = {
|
|
|
521
615
|
else if (existingConfig) configPath = existingConfig;
|
|
522
616
|
else if (ctx.nonInteractive) configPath = "next.config.ts";
|
|
523
617
|
else {
|
|
524
|
-
const selected = await
|
|
618
|
+
const selected = await p.text({
|
|
525
619
|
message: "Path to Next.js config",
|
|
526
620
|
placeholder: "next.config.ts",
|
|
527
621
|
defaultValue: "next.config.ts"
|
|
528
622
|
});
|
|
529
|
-
if (
|
|
623
|
+
if (p.isCancel(selected)) return;
|
|
530
624
|
configPath = selected;
|
|
531
625
|
}
|
|
532
|
-
if (!
|
|
533
|
-
await
|
|
534
|
-
|
|
626
|
+
if (!fsExtra.existsSync(configPath)) {
|
|
627
|
+
await fs.writeFile(configPath, defaultNextConfig);
|
|
628
|
+
p.log.success("Created next config with mochi");
|
|
535
629
|
} else {
|
|
536
630
|
await addMochiToNextConfig(configPath);
|
|
537
|
-
|
|
631
|
+
p.log.success("Added withMochi() to next config");
|
|
538
632
|
}
|
|
539
|
-
ctx.requirePackage("@mochi-css/next");
|
|
633
|
+
ctx.requirePackage(mochiPackage("@mochi-css/next"));
|
|
540
634
|
}
|
|
541
635
|
};
|
|
542
636
|
|
|
@@ -546,11 +640,13 @@ const nextjsPreset = {
|
|
|
546
640
|
id: "nextjs",
|
|
547
641
|
name: "Next.js",
|
|
548
642
|
setup(runner) {
|
|
549
|
-
runner.register(
|
|
550
|
-
|
|
551
|
-
|
|
643
|
+
runner.register(createMochiConfigModule({
|
|
644
|
+
styledId: true,
|
|
645
|
+
tmpDir: ".mochi"
|
|
552
646
|
}));
|
|
647
|
+
runner.register(createPostcssModule({ auto: true }));
|
|
553
648
|
runner.register(nextModule);
|
|
649
|
+
runner.register(createUiFrameworkModule({ auto: true }));
|
|
554
650
|
}
|
|
555
651
|
};
|
|
556
652
|
|
|
@@ -564,30 +660,30 @@ const presets = {
|
|
|
564
660
|
|
|
565
661
|
//#endregion
|
|
566
662
|
//#region src/index.ts
|
|
567
|
-
|
|
663
|
+
program.name("tsuki").description("Add mochi-css to your project").version("4.0.0").addOption(new Option("-p, --preset <preset>", "Preset to use").choices([
|
|
568
664
|
"vite",
|
|
569
665
|
"nextjs",
|
|
570
666
|
"lib"
|
|
571
|
-
])).option("-n, --no-interactive", "Non-interactive mode: skip all prompts (treat as cancelled)").option("--install", "Auto-accept package installation without prompting").option("--postcss [path]", "Enable PostCSS module; optionally specify config path").option("--vite [path]", "Use the given Vite config path instead of prompting").option("--next [path]", "Use the given Next.js config path instead of prompting").action(async (options) => {
|
|
572
|
-
|
|
667
|
+
])).option("-n, --no-interactive", "Non-interactive mode: skip all prompts (treat as cancelled)").option("--install", "Auto-accept package installation without prompting").option("--postcss [path]", "Enable PostCSS module; optionally specify config path").option("--vite [path]", "Use the given Vite config path instead of prompting").option("--next [path]", "Use the given Next.js config path instead of prompting").addOption(new Option("--framework <framework>", "UI framework to install support for").choices(["react"])).action(async (options) => {
|
|
668
|
+
p.intro(pc.cyan("Installing Mochi-CSS..."));
|
|
573
669
|
try {
|
|
574
670
|
const runner = new ModuleRunner();
|
|
575
671
|
const nonInteractive = options.interactive === false;
|
|
576
672
|
let presetId = options.preset;
|
|
577
673
|
if (presetId === void 0) {
|
|
578
674
|
if (nonInteractive) {
|
|
579
|
-
|
|
675
|
+
p.outro(pc.red("Cancelled"));
|
|
580
676
|
return;
|
|
581
677
|
}
|
|
582
|
-
const selected = await
|
|
678
|
+
const selected = await p.select({
|
|
583
679
|
message: "Which framework are you using?",
|
|
584
680
|
options: Object.values(presets).map((preset$1) => ({
|
|
585
681
|
value: preset$1.id,
|
|
586
682
|
label: preset$1.name
|
|
587
683
|
}))
|
|
588
684
|
});
|
|
589
|
-
if (
|
|
590
|
-
|
|
685
|
+
if (p.isCancel(selected)) {
|
|
686
|
+
p.outro(pc.red("Cancelled"));
|
|
591
687
|
return;
|
|
592
688
|
}
|
|
593
689
|
presetId = selected;
|
|
@@ -600,16 +696,18 @@ commander.program.name("tsuki").description("Add mochi-css to your project").ver
|
|
|
600
696
|
else if (presetId === "nextjs") moduleOptions.postcss = true;
|
|
601
697
|
if (options.vite !== void 0) moduleOptions.vite = options.vite;
|
|
602
698
|
if (options.next !== void 0) moduleOptions.next = options.next;
|
|
699
|
+
if (options.framework !== void 0) moduleOptions.framework = options.framework;
|
|
603
700
|
await runner.run({
|
|
604
701
|
nonInteractive,
|
|
605
702
|
autoInstall: options.install ?? false,
|
|
606
703
|
moduleOptions
|
|
607
704
|
});
|
|
608
|
-
|
|
705
|
+
p.outro(pc.green("Done!"));
|
|
609
706
|
} catch (e) {
|
|
610
|
-
if (e instanceof Error)
|
|
707
|
+
if (e instanceof Error) p.outro(pc.red(e.message));
|
|
611
708
|
}
|
|
612
709
|
});
|
|
613
|
-
|
|
710
|
+
program.parse();
|
|
614
711
|
|
|
615
|
-
//#endregion
|
|
712
|
+
//#endregion
|
|
713
|
+
export { };
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mochi-css/tsuki",
|
|
3
3
|
"repository": "git@github.com:Niikelion/mochi-css.git",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"bin": "./dist/index.js",
|
|
7
8
|
"files": [
|
|
8
9
|
"/dist"
|
|
@@ -14,30 +15,24 @@
|
|
|
14
15
|
"manual": "jiti scripts/manual.ts",
|
|
15
16
|
"lint": "eslint src",
|
|
16
17
|
"lint:fix": "eslint src --fix",
|
|
17
|
-
"format": "prettier --write"
|
|
18
|
+
"format": "prettier --write ."
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
|
-
"@
|
|
21
|
-
"@
|
|
21
|
+
"@mochi-css/shared-config": "^2.0.0",
|
|
22
|
+
"@mochi-css/test": "^2.0.0",
|
|
22
23
|
"@types/cross-spawn": "^6.0.6",
|
|
23
24
|
"@types/fs-extra": "^11.0.4",
|
|
24
25
|
"@types/node": "^24.8.1",
|
|
25
|
-
"@vitest/coverage-v8": "^4.0.15",
|
|
26
26
|
"eslint": "^9.39.2",
|
|
27
|
-
"eslint-config-prettier": "^10.1.8",
|
|
28
|
-
"eslint-plugin-prettier": "^5.5.5",
|
|
29
|
-
"jiti": "^2.6.1",
|
|
30
27
|
"prettier": "^3.8.1",
|
|
31
|
-
"tsdown": "^0.15.7",
|
|
32
28
|
"typescript": "^5.9.3",
|
|
33
|
-
"typescript-eslint": "^8.21.0",
|
|
34
|
-
"vite-tsconfig-paths": "^5.1.4",
|
|
35
29
|
"vitest": "^4.0.15"
|
|
36
30
|
},
|
|
37
31
|
"dependencies": {
|
|
38
32
|
"@clack/prompts": "^1.0.1",
|
|
39
33
|
"commander": "^13.1.0",
|
|
40
34
|
"cross-spawn": "^7.0.6",
|
|
35
|
+
"dedent": "^1.7.2",
|
|
41
36
|
"fs-extra": "^11.3.0",
|
|
42
37
|
"magicast": "^0.5.1",
|
|
43
38
|
"package-manager-detector": "^1.6.0",
|