@mochi-css/tsuki 1.1.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 +10 -0
- package/dist/index.js +584 -0
- package/package.json +47 -0
package/README.md
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//#region rolldown:runtime
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
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);
|
|
47
|
+
|
|
48
|
+
//#region src/install.ts
|
|
49
|
+
function onceEvent(emitter, event) {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
emitter.once(event, (...args) => {
|
|
52
|
+
resolve(args);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function spawnProcess(command, args, options) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const child = (0, cross_spawn.default)(command, args, options);
|
|
59
|
+
const onSpawn = () => {
|
|
60
|
+
child.off("error", onError);
|
|
61
|
+
resolve(child);
|
|
62
|
+
};
|
|
63
|
+
const onError = (err) => {
|
|
64
|
+
child.off("spawn", onSpawn);
|
|
65
|
+
reject(err);
|
|
66
|
+
};
|
|
67
|
+
child.once("spawn", onSpawn);
|
|
68
|
+
child.once("error", onError);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async function runInstall(title, command, args, packages) {
|
|
72
|
+
const log = __clack_prompts.taskLog({ title });
|
|
73
|
+
log.message(`${command} ${args.join(" ")}`);
|
|
74
|
+
try {
|
|
75
|
+
const child = await spawnProcess(command, args, { stdio: [
|
|
76
|
+
"inherit",
|
|
77
|
+
"pipe",
|
|
78
|
+
"pipe"
|
|
79
|
+
] });
|
|
80
|
+
const merged = new node_stream.PassThrough();
|
|
81
|
+
let openStreams = 0;
|
|
82
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
83
|
+
if (!stream) continue;
|
|
84
|
+
openStreams++;
|
|
85
|
+
stream.pipe(merged, { end: false });
|
|
86
|
+
stream.on("end", () => {
|
|
87
|
+
if (--openStreams === 0) merged.end();
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const rl = (0, node_readline.createInterface)({
|
|
91
|
+
input: merged,
|
|
92
|
+
crlfDelay: Infinity
|
|
93
|
+
});
|
|
94
|
+
const drain = (async () => {
|
|
95
|
+
for await (const line of rl) log.message(line);
|
|
96
|
+
})();
|
|
97
|
+
const [code] = await onceEvent(child, "close");
|
|
98
|
+
await drain;
|
|
99
|
+
if (code !== 0) throw new Error(`Failed to install packages: ${packages.join(", ")}`);
|
|
100
|
+
log.success(`${packages.join(", ")} has been installed installed!`);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function installPackages(packages) {
|
|
107
|
+
if (packages.length === 0) return;
|
|
108
|
+
const packageManager = await (0, package_manager_detector.detect)({ strategies: [
|
|
109
|
+
"packageManager-field",
|
|
110
|
+
"devEngines-field",
|
|
111
|
+
"lockfile",
|
|
112
|
+
"install-metadata"
|
|
113
|
+
] });
|
|
114
|
+
if (packageManager === null) throw new Error("Could not determine package manager of the project");
|
|
115
|
+
const agent = packageManager.agent;
|
|
116
|
+
const devFlag = agent === "deno" ? "--dev" : "-D";
|
|
117
|
+
const devPackages = packages.filter((pkg) => pkg.dev !== false).map((pkg) => pkg.name);
|
|
118
|
+
const prodPackages = packages.filter((pkg) => pkg.dev === false).map((pkg) => pkg.name);
|
|
119
|
+
const packageList = [...devPackages.map((name) => `${name} (dev)`), ...prodPackages.map((name) => name)].join(", ");
|
|
120
|
+
const confirmed = await __clack_prompts.confirm({ message: `Install the following packages: ${packageList}?` });
|
|
121
|
+
if (__clack_prompts.isCancel(confirmed) || !confirmed) {
|
|
122
|
+
__clack_prompts.log.info("Skipping package installation");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (devPackages.length > 0) {
|
|
126
|
+
const cmd = (0, package_manager_detector.resolveCommand)(agent, "add", [devFlag, ...devPackages]);
|
|
127
|
+
if (cmd === null) throw new Error("Could not prepare install command");
|
|
128
|
+
await runInstall(`Installing dev dependencies: ${devPackages.join(", ")}`, cmd.command, cmd.args, devPackages);
|
|
129
|
+
}
|
|
130
|
+
if (prodPackages.length > 0) {
|
|
131
|
+
const cmd = (0, package_manager_detector.resolveCommand)(agent, "add", prodPackages);
|
|
132
|
+
if (cmd === null) throw new Error("Could not prepare install command");
|
|
133
|
+
await runInstall(`Installing dependencies: ${prodPackages.join(", ")}`, cmd.command, cmd.args, prodPackages);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/runner.ts
|
|
139
|
+
var ModuleRunner = class {
|
|
140
|
+
packages = [];
|
|
141
|
+
modules = [];
|
|
142
|
+
register(module$1) {
|
|
143
|
+
this.modules.push(module$1);
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
async run() {
|
|
147
|
+
const ctx = {
|
|
148
|
+
requirePackage: (name, dev = true) => {
|
|
149
|
+
this.packages.push({
|
|
150
|
+
name,
|
|
151
|
+
dev
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
requirePackages: (packages) => {
|
|
155
|
+
for (const pkg of packages) this.packages.push({
|
|
156
|
+
name: pkg.name,
|
|
157
|
+
dev: pkg.dev ?? true
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
for (const module$1 of this.modules) await module$1.run(ctx);
|
|
162
|
+
if (this.packages.length > 0) await installPackages(this.packages);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/modules/ast.ts
|
|
168
|
+
function getPropKeyName(prop) {
|
|
169
|
+
const key = prop["key"];
|
|
170
|
+
if (typeof key["name"] === "string") return key["name"];
|
|
171
|
+
if (typeof key["value"] === "string") return key["value"];
|
|
172
|
+
}
|
|
173
|
+
function optionsToAstProperties(options) {
|
|
174
|
+
return Object.entries(options).map(([key, value]) => ({
|
|
175
|
+
type: "ObjectProperty",
|
|
176
|
+
key: {
|
|
177
|
+
type: "Identifier",
|
|
178
|
+
name: key
|
|
179
|
+
},
|
|
180
|
+
value: typeof value === "string" ? {
|
|
181
|
+
type: "StringLiteral",
|
|
182
|
+
value
|
|
183
|
+
} : typeof value === "number" ? {
|
|
184
|
+
type: "NumericLiteral",
|
|
185
|
+
value
|
|
186
|
+
} : {
|
|
187
|
+
type: "BooleanLiteral",
|
|
188
|
+
value
|
|
189
|
+
},
|
|
190
|
+
computed: false,
|
|
191
|
+
shorthand: false
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/modules/postcss.ts
|
|
197
|
+
const postcssConfigNames = [
|
|
198
|
+
"postcss.config.mts",
|
|
199
|
+
"postcss.config.ts",
|
|
200
|
+
"postcss.config.mjs",
|
|
201
|
+
"postcss.config.js",
|
|
202
|
+
"postcss.config.cjs",
|
|
203
|
+
".postcssrc.mts",
|
|
204
|
+
".postcssrc.ts",
|
|
205
|
+
".postcssrc.mjs",
|
|
206
|
+
".postcssrc.js",
|
|
207
|
+
".postcssrc.cjs",
|
|
208
|
+
".postcssrc.json",
|
|
209
|
+
".postcssrc.yml",
|
|
210
|
+
".postcssrc.yaml",
|
|
211
|
+
".postcssrc"
|
|
212
|
+
];
|
|
213
|
+
function findPostcssConfig() {
|
|
214
|
+
return postcssConfigNames.find((name) => fs_extra.default.existsSync(name));
|
|
215
|
+
}
|
|
216
|
+
function defaultPostcssConfig(pluginOptions = {}) {
|
|
217
|
+
const entries = Object.entries(pluginOptions);
|
|
218
|
+
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`;
|
|
219
|
+
}
|
|
220
|
+
async function askForPath() {
|
|
221
|
+
const defaultConfig = findPostcssConfig() ?? "postcss.config.js";
|
|
222
|
+
const configPath = await __clack_prompts.text({
|
|
223
|
+
message: "Path to PostCSS config",
|
|
224
|
+
placeholder: defaultConfig,
|
|
225
|
+
defaultValue: defaultConfig
|
|
226
|
+
});
|
|
227
|
+
if (__clack_prompts.isCancel(configPath)) return false;
|
|
228
|
+
return configPath;
|
|
229
|
+
}
|
|
230
|
+
function isProxy(v) {
|
|
231
|
+
return v !== null && typeof v === "object" && typeof v["$ast"] === "object";
|
|
232
|
+
}
|
|
233
|
+
function isObject(v) {
|
|
234
|
+
return v !== null && typeof v === "object";
|
|
235
|
+
}
|
|
236
|
+
function addPluginToNamedVar(mod, varName, pluginName, pluginOptions, configPath) {
|
|
237
|
+
const body = mod.$ast.body;
|
|
238
|
+
for (const stmt of body) {
|
|
239
|
+
if (stmt.type !== "VariableDeclaration") continue;
|
|
240
|
+
for (const decl of stmt.declarations) {
|
|
241
|
+
if (decl.id.type !== "Identifier" || decl.id.name !== varName) continue;
|
|
242
|
+
const init = decl.init;
|
|
243
|
+
if (!init) throw new Error(`Failed to add postcss plugin to ${configPath}`);
|
|
244
|
+
const pluginsProp = init.properties.find((prop) => getPropKeyName(prop) === "plugins");
|
|
245
|
+
if (!pluginsProp) throw new Error(`Failed to find plugins object in ${configPath}`);
|
|
246
|
+
pluginsProp.value.properties.push({
|
|
247
|
+
type: "ObjectProperty",
|
|
248
|
+
key: {
|
|
249
|
+
type: "StringLiteral",
|
|
250
|
+
value: pluginName
|
|
251
|
+
},
|
|
252
|
+
value: {
|
|
253
|
+
type: "ObjectExpression",
|
|
254
|
+
properties: optionsToAstProperties(pluginOptions)
|
|
255
|
+
},
|
|
256
|
+
computed: false,
|
|
257
|
+
shorthand: false
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
throw new Error(`Failed to add postcss plugin to ${configPath}`);
|
|
263
|
+
}
|
|
264
|
+
async function addPostcssPlugin(configPath, pluginName, pluginOptions = {}) {
|
|
265
|
+
const mod = (0, magicast.parseModule)(await fs_promises.default.readFile(configPath, "utf-8"));
|
|
266
|
+
const defaultExport = mod.exports["default"];
|
|
267
|
+
if (isProxy(defaultExport) && defaultExport.$type === "identifier") {
|
|
268
|
+
addPluginToNamedVar(mod, defaultExport.$name, pluginName, pluginOptions, configPath);
|
|
269
|
+
const { code: code$1 } = (0, magicast.generateCode)(mod);
|
|
270
|
+
await fs_promises.default.writeFile(configPath, code$1);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const config = isProxy(defaultExport) && defaultExport.$type === "function-call" ? defaultExport.$args[0] : defaultExport;
|
|
274
|
+
if (!isProxy(config) || config.$type !== "object" || !isObject(config)) throw new Error(`Failed to add postcss plugin to ${configPath}`);
|
|
275
|
+
if ("plugins" in config && config["plugins"] !== void 0 && !isObject(config["plugins"])) throw new Error(`Unrecognized plugins config type in ${configPath}`);
|
|
276
|
+
config["plugins"] ??= {};
|
|
277
|
+
config["plugins"][pluginName] = pluginOptions;
|
|
278
|
+
const { code } = (0, magicast.generateCode)(mod);
|
|
279
|
+
await fs_promises.default.writeFile(configPath, code);
|
|
280
|
+
}
|
|
281
|
+
async function addToConfig(configPath, pluginOptions = {}) {
|
|
282
|
+
if (!fs_extra.default.existsSync(configPath)) {
|
|
283
|
+
await fs_extra.default.writeFile("postcss.config.mts", defaultPostcssConfig(pluginOptions));
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const ext = configPath.split(".").pop();
|
|
287
|
+
if (ext === "json" || path.default.basename(configPath) === ".postcssrc") {
|
|
288
|
+
const config = await fs_extra.default.readJson(configPath);
|
|
289
|
+
if (!isObject(config)) throw new Error("Unrecognized config type in ${configPath}`)");
|
|
290
|
+
config["plugins"] ??= {};
|
|
291
|
+
if (!isObject(config["plugins"])) throw new Error("Unrecognized config type in ${configPath}`)");
|
|
292
|
+
config["plugins"]["@mochi-css/postcss"] = pluginOptions;
|
|
293
|
+
await fs_extra.default.writeJson(configPath, config, { spaces: 2 });
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (ext === "yml" || ext === "yaml") throw new Error("YAML PostCSS config is not supported yet");
|
|
297
|
+
await addPostcssPlugin(configPath, "@mochi-css/postcss", pluginOptions);
|
|
298
|
+
}
|
|
299
|
+
function createPostcssModule(options = {}) {
|
|
300
|
+
const pluginOptions = {};
|
|
301
|
+
if (options.outDir !== void 0) pluginOptions["outDir"] = options.outDir;
|
|
302
|
+
return {
|
|
303
|
+
id: "postcss",
|
|
304
|
+
name: "PostCSS",
|
|
305
|
+
async run(ctx) {
|
|
306
|
+
let configPath;
|
|
307
|
+
if (options.auto) configPath = findPostcssConfig() ?? "postcss.config.mts";
|
|
308
|
+
else {
|
|
309
|
+
const usePostcss = await __clack_prompts.confirm({ message: "Do you use PostCSS?" });
|
|
310
|
+
if (__clack_prompts.isCancel(usePostcss) || !usePostcss) return;
|
|
311
|
+
const selected = await askForPath();
|
|
312
|
+
if (selected === false) return;
|
|
313
|
+
configPath = selected;
|
|
314
|
+
}
|
|
315
|
+
await addToConfig(configPath, pluginOptions);
|
|
316
|
+
__clack_prompts.log.step("Added mochi plugin to the postcss config");
|
|
317
|
+
ctx.requirePackage("@mochi-css/postcss");
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const postcssModule = createPostcssModule();
|
|
322
|
+
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/presets/lib.ts
|
|
325
|
+
const libPreset = {
|
|
326
|
+
id: "lib",
|
|
327
|
+
name: "Library",
|
|
328
|
+
setup(runner) {
|
|
329
|
+
runner.register(createPostcssModule());
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
//#endregion
|
|
334
|
+
//#region src/modules/vite.ts
|
|
335
|
+
const viteConfigNames = [
|
|
336
|
+
"vite.config.ts",
|
|
337
|
+
"vite.config.mts",
|
|
338
|
+
"vite.config.js",
|
|
339
|
+
"vite.config.mjs"
|
|
340
|
+
];
|
|
341
|
+
function findViteConfig() {
|
|
342
|
+
return viteConfigNames.find((name) => fs_extra.default.existsSync(name));
|
|
343
|
+
}
|
|
344
|
+
const defaultViteConfig = `import { defineConfig } from "vite"
|
|
345
|
+
import { mochiCss } from "@mochi-css/vite"
|
|
346
|
+
|
|
347
|
+
export default defineConfig({
|
|
348
|
+
plugins: [mochiCss()],
|
|
349
|
+
})
|
|
350
|
+
`;
|
|
351
|
+
function getPluginsElements(obj, configPath) {
|
|
352
|
+
const existing = obj.properties.find((prop) => getPropKeyName(prop) === "plugins");
|
|
353
|
+
if (existing) {
|
|
354
|
+
const value = existing["value"];
|
|
355
|
+
if (value["type"] !== "ArrayExpression") throw new Error(`Unrecognized plugins config type in ${configPath}`);
|
|
356
|
+
return value["elements"];
|
|
357
|
+
}
|
|
358
|
+
const elements = [];
|
|
359
|
+
obj.properties.push({
|
|
360
|
+
type: "ObjectProperty",
|
|
361
|
+
key: {
|
|
362
|
+
type: "Identifier",
|
|
363
|
+
name: "plugins"
|
|
364
|
+
},
|
|
365
|
+
value: {
|
|
366
|
+
type: "ArrayExpression",
|
|
367
|
+
elements
|
|
368
|
+
},
|
|
369
|
+
computed: false,
|
|
370
|
+
shorthand: false
|
|
371
|
+
});
|
|
372
|
+
return elements;
|
|
373
|
+
}
|
|
374
|
+
function addPluginCallToObj(obj, configPath) {
|
|
375
|
+
getPluginsElements(obj, configPath).push({
|
|
376
|
+
type: "CallExpression",
|
|
377
|
+
callee: {
|
|
378
|
+
type: "Identifier",
|
|
379
|
+
name: "mochiCss"
|
|
380
|
+
},
|
|
381
|
+
arguments: []
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
function addToVitePlugins(mod, configPath) {
|
|
385
|
+
const body = mod.$ast.body;
|
|
386
|
+
const exportDefault = body.find((s) => s.type === "ExportDefaultDeclaration");
|
|
387
|
+
if (!exportDefault) throw new Error(`No default export found in ${configPath}`);
|
|
388
|
+
const decl = exportDefault.declaration;
|
|
389
|
+
if (decl["type"] === "ObjectExpression") {
|
|
390
|
+
addPluginCallToObj(decl, configPath);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (decl["type"] === "CallExpression") {
|
|
394
|
+
const firstArg = decl["arguments"][0];
|
|
395
|
+
if (firstArg?.["type"] === "ObjectExpression") {
|
|
396
|
+
addPluginCallToObj(firstArg, configPath);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (decl["type"] === "Identifier") {
|
|
401
|
+
const varName = decl["name"];
|
|
402
|
+
for (const stmt of body) {
|
|
403
|
+
if (stmt.type !== "VariableDeclaration") continue;
|
|
404
|
+
for (const d of stmt.declarations) {
|
|
405
|
+
if (d.id.type !== "Identifier" || d.id.name !== varName) continue;
|
|
406
|
+
if (d.init?.["type"] !== "ObjectExpression") throw new Error(`Failed to add vite plugin to ${configPath}`);
|
|
407
|
+
addPluginCallToObj(d.init, configPath);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
throw new Error(`Failed to add vite plugin to ${configPath}`);
|
|
413
|
+
}
|
|
414
|
+
async function addMochiToViteConfig(configPath) {
|
|
415
|
+
const mod = (0, magicast.parseModule)(await fs_promises.default.readFile(configPath, "utf-8"));
|
|
416
|
+
mod.imports.$prepend({
|
|
417
|
+
from: "@mochi-css/vite",
|
|
418
|
+
imported: "mochiCss",
|
|
419
|
+
local: "mochiCss"
|
|
420
|
+
});
|
|
421
|
+
addToVitePlugins(mod, configPath);
|
|
422
|
+
const { code } = (0, magicast.generateCode)(mod);
|
|
423
|
+
await fs_promises.default.writeFile(configPath, code);
|
|
424
|
+
}
|
|
425
|
+
const viteModule = {
|
|
426
|
+
id: "vite",
|
|
427
|
+
name: "Vite",
|
|
428
|
+
async run(ctx) {
|
|
429
|
+
const existingConfig = findViteConfig();
|
|
430
|
+
let configPath;
|
|
431
|
+
if (!existingConfig) {
|
|
432
|
+
const selected = await __clack_prompts.text({
|
|
433
|
+
message: "Path to Vite config",
|
|
434
|
+
placeholder: "vite.config.ts",
|
|
435
|
+
defaultValue: "vite.config.ts"
|
|
436
|
+
});
|
|
437
|
+
if (__clack_prompts.isCancel(selected)) return;
|
|
438
|
+
configPath = selected;
|
|
439
|
+
} else configPath = existingConfig;
|
|
440
|
+
if (!fs_extra.default.existsSync(configPath)) {
|
|
441
|
+
await fs_promises.default.writeFile(configPath, defaultViteConfig);
|
|
442
|
+
__clack_prompts.log.step("Created vite config with mochi plugin");
|
|
443
|
+
} else {
|
|
444
|
+
await addMochiToViteConfig(configPath);
|
|
445
|
+
__clack_prompts.log.step("Added mochiCss() to vite config");
|
|
446
|
+
}
|
|
447
|
+
ctx.requirePackage("@mochi-css/vite");
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
//#endregion
|
|
452
|
+
//#region src/presets/vite.ts
|
|
453
|
+
const vitePreset = {
|
|
454
|
+
id: "vite",
|
|
455
|
+
name: "Vite",
|
|
456
|
+
setup(runner) {
|
|
457
|
+
runner.register(createPostcssModule({ outDir: ".mochi" }));
|
|
458
|
+
runner.register(viteModule);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region src/modules/next.ts
|
|
464
|
+
const nextConfigNames = [
|
|
465
|
+
"next.config.ts",
|
|
466
|
+
"next.config.mts",
|
|
467
|
+
"next.config.js",
|
|
468
|
+
"next.config.mjs"
|
|
469
|
+
];
|
|
470
|
+
function findNextConfig() {
|
|
471
|
+
return nextConfigNames.find((name) => fs_extra.default.existsSync(name));
|
|
472
|
+
}
|
|
473
|
+
const defaultNextConfig = `import { withMochi } from "@mochi-css/next"
|
|
474
|
+
|
|
475
|
+
export default withMochi({})
|
|
476
|
+
`;
|
|
477
|
+
function wrapExportDefault(mod, configPath) {
|
|
478
|
+
const exportDefault = mod.$ast.body.find((s) => s.type === "ExportDefaultDeclaration");
|
|
479
|
+
if (!exportDefault) throw new Error(`No default export found in ${configPath}`);
|
|
480
|
+
exportDefault["declaration"] = {
|
|
481
|
+
type: "CallExpression",
|
|
482
|
+
callee: {
|
|
483
|
+
type: "Identifier",
|
|
484
|
+
name: "withMochi"
|
|
485
|
+
},
|
|
486
|
+
arguments: [exportDefault.declaration]
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
async function addMochiToNextConfig(configPath) {
|
|
490
|
+
const mod = (0, magicast.parseModule)(await fs_promises.default.readFile(configPath, "utf-8"));
|
|
491
|
+
mod.imports.$prepend({
|
|
492
|
+
from: "@mochi-css/next",
|
|
493
|
+
imported: "withMochi",
|
|
494
|
+
local: "withMochi"
|
|
495
|
+
});
|
|
496
|
+
wrapExportDefault(mod, configPath);
|
|
497
|
+
const { code } = (0, magicast.generateCode)(mod);
|
|
498
|
+
await fs_promises.default.writeFile(configPath, code);
|
|
499
|
+
}
|
|
500
|
+
const nextModule = {
|
|
501
|
+
id: "next",
|
|
502
|
+
name: "Next.js",
|
|
503
|
+
async run(ctx) {
|
|
504
|
+
const existingConfig = findNextConfig();
|
|
505
|
+
let configPath;
|
|
506
|
+
if (!existingConfig) {
|
|
507
|
+
const selected = await __clack_prompts.text({
|
|
508
|
+
message: "Path to Next.js config",
|
|
509
|
+
placeholder: "next.config.ts",
|
|
510
|
+
defaultValue: "next.config.ts"
|
|
511
|
+
});
|
|
512
|
+
if (__clack_prompts.isCancel(selected)) return;
|
|
513
|
+
configPath = selected;
|
|
514
|
+
} else configPath = existingConfig;
|
|
515
|
+
if (!fs_extra.default.existsSync(configPath)) {
|
|
516
|
+
await fs_promises.default.writeFile(configPath, defaultNextConfig);
|
|
517
|
+
__clack_prompts.log.step("Created next config with mochi");
|
|
518
|
+
} else {
|
|
519
|
+
await addMochiToNextConfig(configPath);
|
|
520
|
+
__clack_prompts.log.step("Added withMochi() to next config");
|
|
521
|
+
}
|
|
522
|
+
ctx.requirePackage("@mochi-css/next");
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
//#endregion
|
|
527
|
+
//#region src/presets/nextjs.ts
|
|
528
|
+
const nextjsPreset = {
|
|
529
|
+
id: "nextjs",
|
|
530
|
+
name: "Next.js",
|
|
531
|
+
setup(runner) {
|
|
532
|
+
runner.register(createPostcssModule({
|
|
533
|
+
outDir: ".mochi",
|
|
534
|
+
auto: true
|
|
535
|
+
}));
|
|
536
|
+
runner.register(nextModule);
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
//#endregion
|
|
541
|
+
//#region src/presets/index.ts
|
|
542
|
+
const presets = {
|
|
543
|
+
lib: libPreset,
|
|
544
|
+
vite: vitePreset,
|
|
545
|
+
nextjs: nextjsPreset
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
//#endregion
|
|
549
|
+
//#region src/index.ts
|
|
550
|
+
commander.program.name("tsuki").description("Add mochi-css to your project").version("1.1.0").addOption(new commander.Option("-p, --preset <preset>", "Preset to use").choices([
|
|
551
|
+
"vite",
|
|
552
|
+
"nextjs",
|
|
553
|
+
"lib"
|
|
554
|
+
])).action(async (options) => {
|
|
555
|
+
__clack_prompts.intro(picocolors.default.cyan("Installing Mochi-CSS..."));
|
|
556
|
+
try {
|
|
557
|
+
const runner = new ModuleRunner();
|
|
558
|
+
let presetId = options.preset;
|
|
559
|
+
if (presetId === void 0) {
|
|
560
|
+
const selected = await __clack_prompts.select({
|
|
561
|
+
message: "Which framework are you using?",
|
|
562
|
+
options: Object.values(presets).map((preset$1) => ({
|
|
563
|
+
value: preset$1.id,
|
|
564
|
+
label: preset$1.name
|
|
565
|
+
}))
|
|
566
|
+
});
|
|
567
|
+
if (__clack_prompts.isCancel(selected)) {
|
|
568
|
+
__clack_prompts.outro(picocolors.default.red("Cancelled"));
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
presetId = selected;
|
|
572
|
+
}
|
|
573
|
+
const preset = presets[presetId];
|
|
574
|
+
if (!preset) throw new Error(`Unknown preset: ${presetId}`);
|
|
575
|
+
preset.setup(runner);
|
|
576
|
+
await runner.run();
|
|
577
|
+
__clack_prompts.outro(picocolors.default.green("Done!"));
|
|
578
|
+
} catch (e) {
|
|
579
|
+
if (e instanceof Error) __clack_prompts.outro(picocolors.default.red(e.message));
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
commander.program.parse();
|
|
583
|
+
|
|
584
|
+
//#endregion
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mochi-css/tsuki",
|
|
3
|
+
"repository": "git@github.com:Niikelion/mochi-css.git",
|
|
4
|
+
"version": "1.1.0",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": "./dist/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"/dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc --noEmit && tsdown",
|
|
12
|
+
"test": "vitest",
|
|
13
|
+
"coverage": "vitest run --coverage",
|
|
14
|
+
"smoke": "jiti scripts/smoke.ts",
|
|
15
|
+
"manual": "jiti scripts/manual.ts",
|
|
16
|
+
"lint": "eslint src",
|
|
17
|
+
"lint:fix": "eslint src --fix",
|
|
18
|
+
"format": "prettier --write"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@eslint/js": "^9.18.0",
|
|
22
|
+
"@gamedev-sensei/ts-config": "^2.1.0",
|
|
23
|
+
"@types/cross-spawn": "^6.0.6",
|
|
24
|
+
"@types/fs-extra": "^11.0.4",
|
|
25
|
+
"@types/node": "^24.8.1",
|
|
26
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
27
|
+
"eslint": "^9.39.2",
|
|
28
|
+
"eslint-config-prettier": "^10.1.8",
|
|
29
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
30
|
+
"jiti": "^2.6.1",
|
|
31
|
+
"prettier": "^3.8.1",
|
|
32
|
+
"tsdown": "^0.15.7",
|
|
33
|
+
"typescript": "^5.9.3",
|
|
34
|
+
"typescript-eslint": "^8.21.0",
|
|
35
|
+
"vite-tsconfig-paths": "^5.1.4",
|
|
36
|
+
"vitest": "^4.0.15"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@clack/prompts": "^1.0.1",
|
|
40
|
+
"commander": "^13.1.0",
|
|
41
|
+
"cross-spawn": "^7.0.6",
|
|
42
|
+
"fs-extra": "^11.3.0",
|
|
43
|
+
"magicast": "^0.5.1",
|
|
44
|
+
"package-manager-detector": "^1.6.0",
|
|
45
|
+
"picocolors": "^1.1.1"
|
|
46
|
+
}
|
|
47
|
+
}
|