@mochi-css/tsuki 2.1.0 → 3.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 +277 -151
- package/package.json +5 -10
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`, `extractors`, `plugins`, 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("3.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,185 @@ 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/config"
|
|
446
|
+
|
|
447
|
+
export default defineConfig({})
|
|
448
|
+
`;
|
|
449
|
+
function defaultMochiConfigWithOptions(tmpDir, styledId) {
|
|
450
|
+
const lines = [];
|
|
451
|
+
if (tmpDir !== void 0) lines.push(` tmpDir: ${JSON.stringify(tmpDir)},`);
|
|
452
|
+
if (styledId) lines.push(` plugins: [styledIdPlugin()],`);
|
|
453
|
+
if (!styledId && lines.length === 0) return defaultMochiConfigBase;
|
|
454
|
+
return `${styledId ? dedent`
|
|
455
|
+
import { defineConfig } from "@mochi-css/config"
|
|
456
|
+
import { styledIdPlugin } from "@mochi-css/builder"
|
|
457
|
+
` : `import { defineConfig } from "@mochi-css/config"`}\n\nexport default defineConfig({\n${lines.join("\n")}\n})\n`;
|
|
458
|
+
}
|
|
459
|
+
function addStyledIdPluginToObj(obj, configPath) {
|
|
460
|
+
getPluginsElements(obj, configPath).push({
|
|
461
|
+
type: "CallExpression",
|
|
462
|
+
callee: {
|
|
463
|
+
type: "Identifier",
|
|
464
|
+
name: "styledIdPlugin"
|
|
465
|
+
},
|
|
466
|
+
arguments: []
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
function addStyledIdToAst(mod, configPath) {
|
|
470
|
+
const exportDefault = mod.$ast.body.find((s) => s.type === "ExportDefaultDeclaration");
|
|
471
|
+
if (!exportDefault) throw new Error(`No default export found in ${configPath}`);
|
|
472
|
+
const decl = exportDefault.declaration;
|
|
473
|
+
if (decl["type"] === "ObjectExpression") {
|
|
474
|
+
addStyledIdPluginToObj(decl, configPath);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (decl["type"] === "CallExpression") {
|
|
478
|
+
const firstArg = decl["arguments"][0];
|
|
479
|
+
if (firstArg?.["type"] === "ObjectExpression") {
|
|
480
|
+
addStyledIdPluginToObj(firstArg, configPath);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
throw new Error(`Failed to add styledIdPlugin to ${configPath}`);
|
|
485
|
+
}
|
|
486
|
+
async function addStyledIdToExistingConfig(configPath) {
|
|
487
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
488
|
+
if (content.includes("styledIdPlugin")) return;
|
|
489
|
+
const mod = parseModule(content);
|
|
490
|
+
mod.imports.$prepend({
|
|
491
|
+
from: "@mochi-css/builder",
|
|
492
|
+
imported: "styledIdPlugin",
|
|
493
|
+
local: "styledIdPlugin"
|
|
494
|
+
});
|
|
495
|
+
addStyledIdToAst(mod, configPath);
|
|
496
|
+
const { code } = generateCode(mod);
|
|
497
|
+
await fs.writeFile(configPath, code);
|
|
498
|
+
}
|
|
499
|
+
function getConfigObject(mod) {
|
|
500
|
+
const exportDefault = mod.$ast.body.find((s) => s.type === "ExportDefaultDeclaration");
|
|
501
|
+
if (!exportDefault) return void 0;
|
|
502
|
+
const decl = exportDefault.declaration;
|
|
503
|
+
if (decl["type"] === "ObjectExpression") return decl;
|
|
504
|
+
if (decl["type"] === "CallExpression") {
|
|
505
|
+
const firstArg = decl["arguments"][0];
|
|
506
|
+
if (firstArg?.["type"] === "ObjectExpression") return firstArg;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function addTmpDirToExistingConfig(configPath, tmpDir) {
|
|
510
|
+
const mod = parseModule(await fs.readFile(configPath, "utf-8"));
|
|
511
|
+
const obj = getConfigObject(mod);
|
|
512
|
+
if (!obj) throw new Error(`Failed to add tmpDir to ${configPath}`);
|
|
513
|
+
if (obj["properties"].some((prop) => getPropKeyName(prop) === "tmpDir")) return;
|
|
514
|
+
obj["properties"] = [{
|
|
515
|
+
type: "ObjectProperty",
|
|
516
|
+
key: {
|
|
517
|
+
type: "StringLiteral",
|
|
518
|
+
value: "tmpDir"
|
|
519
|
+
},
|
|
520
|
+
value: {
|
|
521
|
+
type: "StringLiteral",
|
|
522
|
+
value: tmpDir
|
|
523
|
+
},
|
|
524
|
+
computed: false,
|
|
525
|
+
shorthand: false
|
|
526
|
+
}, ...obj["properties"]];
|
|
527
|
+
const { code } = generateCode(mod);
|
|
528
|
+
await fs.writeFile(configPath, code);
|
|
529
|
+
}
|
|
530
|
+
function createMochiConfigModule(options = {}) {
|
|
531
|
+
const { styledId = false, tmpDir } = options;
|
|
532
|
+
return {
|
|
533
|
+
id: "mochi-config",
|
|
534
|
+
name: "Mochi Config",
|
|
535
|
+
async run(ctx) {
|
|
536
|
+
const existing = findMochiConfig();
|
|
537
|
+
if (!existing) {
|
|
538
|
+
await fs.writeFile("mochi.config.ts", defaultMochiConfigWithOptions(tmpDir, styledId));
|
|
539
|
+
p.log.success("Created mochi.config.ts");
|
|
540
|
+
} else {
|
|
541
|
+
if (tmpDir !== void 0) try {
|
|
542
|
+
await addTmpDirToExistingConfig(existing, tmpDir);
|
|
543
|
+
p.log.success(`Added tmpDir to ${existing}`);
|
|
544
|
+
} catch {
|
|
545
|
+
p.log.warn(`Could not automatically add tmpDir to ${existing} — add it manually`);
|
|
546
|
+
}
|
|
547
|
+
if (styledId) try {
|
|
548
|
+
await addStyledIdToExistingConfig(existing);
|
|
549
|
+
p.log.success("Added styledIdPlugin to mochi.config.ts");
|
|
550
|
+
} catch {
|
|
551
|
+
p.log.warn(`Could not automatically add styledIdPlugin to ${existing} — add it manually`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
ctx.requirePackage(mochiPackage("@mochi-css/config"));
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/modules/uiFramework.ts
|
|
561
|
+
function createUiFrameworkModule(options = {}) {
|
|
562
|
+
return {
|
|
563
|
+
id: "ui-framework",
|
|
564
|
+
name: "UI Framework",
|
|
565
|
+
async run(ctx) {
|
|
566
|
+
const { framework: cliOption } = ctx.moduleOptions;
|
|
567
|
+
let useReact = options.auto === true || cliOption === "react";
|
|
568
|
+
if (useReact) {
|
|
569
|
+
ctx.requirePackage(mochiPackage("@mochi-css/react"), false);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (ctx.nonInteractive) return;
|
|
573
|
+
const confirmed = await p.confirm({ message: "Do you use React?" });
|
|
574
|
+
if (p.isCancel(confirmed) || !confirmed) return;
|
|
575
|
+
useReact = true;
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
464
580
|
//#endregion
|
|
465
581
|
//#region src/presets/vite.ts
|
|
466
582
|
const vitePreset = {
|
|
467
583
|
id: "vite",
|
|
468
584
|
name: "Vite",
|
|
469
585
|
setup(runner) {
|
|
470
|
-
runner.register(
|
|
586
|
+
runner.register(createMochiConfigModule({
|
|
587
|
+
styledId: true,
|
|
588
|
+
tmpDir: ".mochi"
|
|
589
|
+
}));
|
|
590
|
+
runner.register(createPostcssModule());
|
|
471
591
|
runner.register(viteModule);
|
|
592
|
+
runner.register(createUiFrameworkModule());
|
|
472
593
|
}
|
|
473
594
|
};
|
|
474
595
|
|
|
@@ -481,11 +602,12 @@ const nextConfigNames = [
|
|
|
481
602
|
"next.config.mjs"
|
|
482
603
|
];
|
|
483
604
|
function findNextConfig() {
|
|
484
|
-
return nextConfigNames.find((name) =>
|
|
605
|
+
return nextConfigNames.find((name) => fsExtra.existsSync(name));
|
|
485
606
|
}
|
|
486
|
-
const defaultNextConfig = `
|
|
607
|
+
const defaultNextConfig = dedent`
|
|
608
|
+
import { withMochi } from "@mochi-css/next"
|
|
487
609
|
|
|
488
|
-
export default withMochi({})
|
|
610
|
+
export default withMochi({})
|
|
489
611
|
`;
|
|
490
612
|
function wrapExportDefault(mod, configPath) {
|
|
491
613
|
const exportDefault = mod.$ast.body.find((s) => s.type === "ExportDefaultDeclaration");
|
|
@@ -500,15 +622,15 @@ function wrapExportDefault(mod, configPath) {
|
|
|
500
622
|
};
|
|
501
623
|
}
|
|
502
624
|
async function addMochiToNextConfig(configPath) {
|
|
503
|
-
const mod =
|
|
625
|
+
const mod = parseModule(await fs.readFile(configPath, "utf-8"));
|
|
504
626
|
mod.imports.$prepend({
|
|
505
627
|
from: "@mochi-css/next",
|
|
506
628
|
imported: "withMochi",
|
|
507
629
|
local: "withMochi"
|
|
508
630
|
});
|
|
509
631
|
wrapExportDefault(mod, configPath);
|
|
510
|
-
const { code } =
|
|
511
|
-
await
|
|
632
|
+
const { code } = generateCode(mod);
|
|
633
|
+
await fs.writeFile(configPath, code);
|
|
512
634
|
}
|
|
513
635
|
const nextModule = {
|
|
514
636
|
id: "next",
|
|
@@ -521,22 +643,22 @@ const nextModule = {
|
|
|
521
643
|
else if (existingConfig) configPath = existingConfig;
|
|
522
644
|
else if (ctx.nonInteractive) configPath = "next.config.ts";
|
|
523
645
|
else {
|
|
524
|
-
const selected = await
|
|
646
|
+
const selected = await p.text({
|
|
525
647
|
message: "Path to Next.js config",
|
|
526
648
|
placeholder: "next.config.ts",
|
|
527
649
|
defaultValue: "next.config.ts"
|
|
528
650
|
});
|
|
529
|
-
if (
|
|
651
|
+
if (p.isCancel(selected)) return;
|
|
530
652
|
configPath = selected;
|
|
531
653
|
}
|
|
532
|
-
if (!
|
|
533
|
-
await
|
|
534
|
-
|
|
654
|
+
if (!fsExtra.existsSync(configPath)) {
|
|
655
|
+
await fs.writeFile(configPath, defaultNextConfig);
|
|
656
|
+
p.log.success("Created next config with mochi");
|
|
535
657
|
} else {
|
|
536
658
|
await addMochiToNextConfig(configPath);
|
|
537
|
-
|
|
659
|
+
p.log.success("Added withMochi() to next config");
|
|
538
660
|
}
|
|
539
|
-
ctx.requirePackage("@mochi-css/next");
|
|
661
|
+
ctx.requirePackage(mochiPackage("@mochi-css/next"));
|
|
540
662
|
}
|
|
541
663
|
};
|
|
542
664
|
|
|
@@ -546,11 +668,13 @@ const nextjsPreset = {
|
|
|
546
668
|
id: "nextjs",
|
|
547
669
|
name: "Next.js",
|
|
548
670
|
setup(runner) {
|
|
549
|
-
runner.register(
|
|
550
|
-
|
|
551
|
-
|
|
671
|
+
runner.register(createMochiConfigModule({
|
|
672
|
+
styledId: true,
|
|
673
|
+
tmpDir: ".mochi"
|
|
552
674
|
}));
|
|
675
|
+
runner.register(createPostcssModule({ auto: true }));
|
|
553
676
|
runner.register(nextModule);
|
|
677
|
+
runner.register(createUiFrameworkModule({ auto: true }));
|
|
554
678
|
}
|
|
555
679
|
};
|
|
556
680
|
|
|
@@ -564,30 +688,30 @@ const presets = {
|
|
|
564
688
|
|
|
565
689
|
//#endregion
|
|
566
690
|
//#region src/index.ts
|
|
567
|
-
|
|
691
|
+
program.name("tsuki").description("Add mochi-css to your project").version("3.0.0").addOption(new Option("-p, --preset <preset>", "Preset to use").choices([
|
|
568
692
|
"vite",
|
|
569
693
|
"nextjs",
|
|
570
694
|
"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
|
-
|
|
695
|
+
])).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) => {
|
|
696
|
+
p.intro(pc.cyan("Installing Mochi-CSS..."));
|
|
573
697
|
try {
|
|
574
698
|
const runner = new ModuleRunner();
|
|
575
699
|
const nonInteractive = options.interactive === false;
|
|
576
700
|
let presetId = options.preset;
|
|
577
701
|
if (presetId === void 0) {
|
|
578
702
|
if (nonInteractive) {
|
|
579
|
-
|
|
703
|
+
p.outro(pc.red("Cancelled"));
|
|
580
704
|
return;
|
|
581
705
|
}
|
|
582
|
-
const selected = await
|
|
706
|
+
const selected = await p.select({
|
|
583
707
|
message: "Which framework are you using?",
|
|
584
708
|
options: Object.values(presets).map((preset$1) => ({
|
|
585
709
|
value: preset$1.id,
|
|
586
710
|
label: preset$1.name
|
|
587
711
|
}))
|
|
588
712
|
});
|
|
589
|
-
if (
|
|
590
|
-
|
|
713
|
+
if (p.isCancel(selected)) {
|
|
714
|
+
p.outro(pc.red("Cancelled"));
|
|
591
715
|
return;
|
|
592
716
|
}
|
|
593
717
|
presetId = selected;
|
|
@@ -600,16 +724,18 @@ commander.program.name("tsuki").description("Add mochi-css to your project").ver
|
|
|
600
724
|
else if (presetId === "nextjs") moduleOptions.postcss = true;
|
|
601
725
|
if (options.vite !== void 0) moduleOptions.vite = options.vite;
|
|
602
726
|
if (options.next !== void 0) moduleOptions.next = options.next;
|
|
727
|
+
if (options.framework !== void 0) moduleOptions.framework = options.framework;
|
|
603
728
|
await runner.run({
|
|
604
729
|
nonInteractive,
|
|
605
730
|
autoInstall: options.install ?? false,
|
|
606
731
|
moduleOptions
|
|
607
732
|
});
|
|
608
|
-
|
|
733
|
+
p.outro(pc.green("Done!"));
|
|
609
734
|
} catch (e) {
|
|
610
|
-
if (e instanceof Error)
|
|
735
|
+
if (e instanceof Error) p.outro(pc.red(e.message));
|
|
611
736
|
}
|
|
612
737
|
});
|
|
613
|
-
|
|
738
|
+
program.parse();
|
|
614
739
|
|
|
615
|
-
//#endregion
|
|
740
|
+
//#endregion
|
|
741
|
+
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": "3.0.0",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"bin": "./dist/index.js",
|
|
7
8
|
"files": [
|
|
8
9
|
"/dist"
|
|
@@ -17,27 +18,21 @@
|
|
|
17
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",
|