@buding0904/vitepad 0.2.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/LICENSE +21 -0
- package/README.md +196 -0
- package/dist/cli.cjs +785 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +774 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +782 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +770 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/frameworks.d.ts +28 -0
- package/dist/runtime/frameworks.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +17 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/package.json +73 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs2 = require('fs/promises');
|
|
4
|
+
var os = require('os');
|
|
5
|
+
var path2 = require('path');
|
|
6
|
+
var process2 = require('process');
|
|
7
|
+
var url = require('url');
|
|
8
|
+
var vite = require('vite');
|
|
9
|
+
var pc2 = require('picocolors');
|
|
10
|
+
var child_process = require('child_process');
|
|
11
|
+
var module$1 = require('module');
|
|
12
|
+
|
|
13
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
14
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
|
|
16
|
+
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
17
|
+
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
18
|
+
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
19
|
+
var process2__default = /*#__PURE__*/_interopDefault(process2);
|
|
20
|
+
var pc2__default = /*#__PURE__*/_interopDefault(pc2);
|
|
21
|
+
|
|
22
|
+
// src/runtime/index.ts
|
|
23
|
+
var packageRoot = path2__default.default.resolve(url.fileURLToPath(new URL("..", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))));
|
|
24
|
+
var linkedPeerPackages = ["vite"];
|
|
25
|
+
var log = {
|
|
26
|
+
framework(requested, resolved) {
|
|
27
|
+
console.log(`
|
|
28
|
+
${pc2__default.default.cyan("vitepad")} ${pc2__default.default.bold("framework")} ${pc2__default.default.gray(requested)} ${pc2__default.default.gray("->")} ${pc2__default.default.green(resolved)}`);
|
|
29
|
+
},
|
|
30
|
+
install(packages) {
|
|
31
|
+
console.log(` ${pc2__default.default.gray("install")} ${packages.map((pkg) => pc2__default.default.cyan(pkg)).join(pc2__default.default.gray(", "))}`);
|
|
32
|
+
},
|
|
33
|
+
done(message) {
|
|
34
|
+
console.log(` ${pc2__default.default.green(message)}`);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
async function resolveFramework(spec, options) {
|
|
38
|
+
if (spec.name === "auto") {
|
|
39
|
+
throw new Error("Internal error: unresolved framework auto.");
|
|
40
|
+
}
|
|
41
|
+
if (spec.name === "vanilla") {
|
|
42
|
+
return {
|
|
43
|
+
name: "vanilla",
|
|
44
|
+
version: "local",
|
|
45
|
+
requested: "vanilla",
|
|
46
|
+
cacheStatus: "local",
|
|
47
|
+
aliases: [],
|
|
48
|
+
packageLinks: packageLinks(packageNodeModules(), ["tailwindcss"])
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const resolved = await resolveFrameworkPackages(spec.name, spec.version);
|
|
52
|
+
const cacheDir = frameworkCacheDir(spec.name, resolved.version);
|
|
53
|
+
const nodeModules = path2__default.default.join(cacheDir, "node_modules");
|
|
54
|
+
const installed = await isInstalled(cacheDir, resolved.packages);
|
|
55
|
+
if (options.forceInstall && await pathExists(cacheDir)) {
|
|
56
|
+
await fs2__default.default.rm(cacheDir, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
log.framework(`${spec.name}@${spec.version}`, `${spec.name}@${resolved.version}`);
|
|
59
|
+
const cacheStatus = options.forceInstall ? "miss" : installed ? "hit" : "miss";
|
|
60
|
+
if (options.forceInstall || !installed) {
|
|
61
|
+
log.install(resolved.packages);
|
|
62
|
+
await installFrameworkCache(cacheDir, resolved.packages);
|
|
63
|
+
}
|
|
64
|
+
await linkPeerPackages(cacheDir);
|
|
65
|
+
return {
|
|
66
|
+
name: spec.name,
|
|
67
|
+
version: resolved.version,
|
|
68
|
+
requested: `${spec.name}@${spec.version}`,
|
|
69
|
+
cacheStatus,
|
|
70
|
+
cacheDir,
|
|
71
|
+
aliases: [],
|
|
72
|
+
packageLinks: [
|
|
73
|
+
...packageLinks(packageNodeModules(), ["tailwindcss"]),
|
|
74
|
+
...packageLinks(nodeModules, frameworkRuntimePackages(spec.name))
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function resolveFrameworkPackages(framework, version) {
|
|
79
|
+
const specs = frameworkPackageSpecs(framework, version);
|
|
80
|
+
const resolved = await Promise.all(specs.map(async (spec) => {
|
|
81
|
+
const parsed = splitPackageSpec(spec);
|
|
82
|
+
const resolvedVersion = await resolvePackageVersion(parsed.name, parsed.version);
|
|
83
|
+
return {
|
|
84
|
+
name: parsed.name,
|
|
85
|
+
version: resolvedVersion,
|
|
86
|
+
spec: `${parsed.name}@${resolvedVersion}`
|
|
87
|
+
};
|
|
88
|
+
}));
|
|
89
|
+
const primary = splitPackageSpec(specs[0]).name;
|
|
90
|
+
const primaryVersion = resolved.find((pkg) => pkg.name === primary)?.version;
|
|
91
|
+
if (!primaryVersion) {
|
|
92
|
+
throw new Error(`Failed to resolve ${framework}@${version}.`);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
version: primaryVersion,
|
|
96
|
+
packages: resolved.map((pkg) => pkg.spec)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function frameworkDedupe(framework) {
|
|
100
|
+
return frameworkRuntimePackages(framework);
|
|
101
|
+
}
|
|
102
|
+
function frameworkOptimizeDeps(framework) {
|
|
103
|
+
switch (framework) {
|
|
104
|
+
case "react":
|
|
105
|
+
return ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"];
|
|
106
|
+
case "preact":
|
|
107
|
+
return ["preact", "preact/jsx-runtime", "preact/hooks"];
|
|
108
|
+
case "solid":
|
|
109
|
+
return ["solid-js", "solid-js/web"];
|
|
110
|
+
case "vue":
|
|
111
|
+
return ["vue"];
|
|
112
|
+
case "svelte":
|
|
113
|
+
case "vanilla":
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function loadFrameworkPlugins(framework) {
|
|
118
|
+
if (framework.name === "vanilla") {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
if (!framework.cacheDir) {
|
|
122
|
+
throw new Error(`Missing framework cache for ${framework.name}.`);
|
|
123
|
+
}
|
|
124
|
+
const plugins = [];
|
|
125
|
+
for (const pluginPackage of frameworkPluginPackages(framework.name)) {
|
|
126
|
+
const mod = await importCachePackage(framework.cacheDir, pluginPackage);
|
|
127
|
+
appendPlugin(plugins, createFrameworkPlugin(pluginPackage, mod));
|
|
128
|
+
}
|
|
129
|
+
return plugins;
|
|
130
|
+
}
|
|
131
|
+
function createFrameworkPlugin(pluginPackage, mod) {
|
|
132
|
+
const factory = frameworkPluginFactory(pluginPackage, mod);
|
|
133
|
+
return factory();
|
|
134
|
+
}
|
|
135
|
+
function frameworkPluginFactory(pluginPackage, mod) {
|
|
136
|
+
if (pluginPackage === "@sveltejs/vite-plugin-svelte" && typeof mod.svelte === "function") {
|
|
137
|
+
return mod.svelte;
|
|
138
|
+
}
|
|
139
|
+
if (pluginPackage === "@preact/preset-vite" && typeof mod.preact === "function") {
|
|
140
|
+
return mod.preact;
|
|
141
|
+
}
|
|
142
|
+
if (typeof mod.default === "function") {
|
|
143
|
+
return mod.default;
|
|
144
|
+
}
|
|
145
|
+
if (mod.default && typeof mod.default.default === "function") {
|
|
146
|
+
return mod.default.default;
|
|
147
|
+
}
|
|
148
|
+
throw new Error(`Failed to load Vite plugin ${pluginPackage}: no callable plugin export found.`);
|
|
149
|
+
}
|
|
150
|
+
function appendPlugin(plugins, plugin) {
|
|
151
|
+
if (Array.isArray(plugin)) {
|
|
152
|
+
plugins.push(...plugin);
|
|
153
|
+
} else {
|
|
154
|
+
plugins.push(plugin);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function frameworkPackageSpecs(framework, version) {
|
|
158
|
+
switch (framework) {
|
|
159
|
+
case "react":
|
|
160
|
+
return [`react@${version}`, `react-dom@${version}`, "@vitejs/plugin-react@latest"];
|
|
161
|
+
case "preact":
|
|
162
|
+
return [`preact@${version}`, "@preact/preset-vite@latest"];
|
|
163
|
+
case "solid":
|
|
164
|
+
return [`solid-js@${version}`, "vite-plugin-solid@latest"];
|
|
165
|
+
case "vue":
|
|
166
|
+
return [`vue@${version}`, "@vitejs/plugin-vue@latest", "@vitejs/plugin-vue-jsx@latest"];
|
|
167
|
+
case "svelte":
|
|
168
|
+
return [`svelte@${version}`, "@sveltejs/vite-plugin-svelte@latest"];
|
|
169
|
+
case "vanilla":
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function resolvePackageVersion(name, range) {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
const child = child_process.spawn("npm", ["view", `${name}@${range}`, "version", "--json"], {
|
|
176
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
177
|
+
});
|
|
178
|
+
let stdout = "";
|
|
179
|
+
let stderr = "";
|
|
180
|
+
child.stdout.on("data", (chunk) => {
|
|
181
|
+
stdout += chunk;
|
|
182
|
+
});
|
|
183
|
+
child.stderr.on("data", (chunk) => {
|
|
184
|
+
stderr += chunk;
|
|
185
|
+
});
|
|
186
|
+
child.on("error", reject);
|
|
187
|
+
child.on("exit", (code) => {
|
|
188
|
+
if (code !== 0) {
|
|
189
|
+
reject(new Error(`npm view failed for ${name}@${range}
|
|
190
|
+
${stderr.trim()}`));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const parsed = JSON.parse(stdout.trim());
|
|
195
|
+
const version = Array.isArray(parsed) ? parsed[parsed.length - 1] : parsed;
|
|
196
|
+
if (typeof version !== "string" || !version) {
|
|
197
|
+
throw new Error(`Unexpected npm view output: ${stdout}`);
|
|
198
|
+
}
|
|
199
|
+
resolve(version);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
reject(error);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
function frameworkRuntimePackages(framework) {
|
|
207
|
+
switch (framework) {
|
|
208
|
+
case "react":
|
|
209
|
+
return ["react", "react-dom"];
|
|
210
|
+
case "preact":
|
|
211
|
+
return ["preact"];
|
|
212
|
+
case "solid":
|
|
213
|
+
return ["solid-js"];
|
|
214
|
+
case "vue":
|
|
215
|
+
return ["vue"];
|
|
216
|
+
case "svelte":
|
|
217
|
+
return ["svelte"];
|
|
218
|
+
case "vanilla":
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function frameworkPluginPackages(framework) {
|
|
223
|
+
switch (framework) {
|
|
224
|
+
case "react":
|
|
225
|
+
return ["@vitejs/plugin-react"];
|
|
226
|
+
case "preact":
|
|
227
|
+
return ["@preact/preset-vite"];
|
|
228
|
+
case "solid":
|
|
229
|
+
return ["vite-plugin-solid"];
|
|
230
|
+
case "vue":
|
|
231
|
+
return ["@vitejs/plugin-vue", "@vitejs/plugin-vue-jsx"];
|
|
232
|
+
case "svelte":
|
|
233
|
+
return ["@sveltejs/vite-plugin-svelte"];
|
|
234
|
+
case "vanilla":
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function packageLinks(nodeModules, packageNames) {
|
|
239
|
+
return packageNames.map((packageName) => ({
|
|
240
|
+
name: packageName,
|
|
241
|
+
source: path2__default.default.join(nodeModules, packageName)
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
function packageNodeModules() {
|
|
245
|
+
return path2__default.default.join(packageRoot, "node_modules");
|
|
246
|
+
}
|
|
247
|
+
function frameworkCacheDir(framework, version) {
|
|
248
|
+
const base = process.env.VITEPAD_CACHE_DIR || (process.env.XDG_CACHE_HOME ? path2__default.default.join(process.env.XDG_CACHE_HOME, "vitepad") : path2__default.default.join(os__default.default.homedir(), ".cache", "vitepad"));
|
|
249
|
+
return path2__default.default.join(base, "frameworks", `${framework}-${sanitizeCacheKey(version)}`);
|
|
250
|
+
}
|
|
251
|
+
function sanitizeCacheKey(value) {
|
|
252
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
253
|
+
}
|
|
254
|
+
async function isInstalled(cacheDir, packages) {
|
|
255
|
+
if (!await pathExists(path2__default.default.join(cacheDir, "node_modules"))) return false;
|
|
256
|
+
for (const pkg of packages) {
|
|
257
|
+
const { name } = splitPackageSpec(pkg);
|
|
258
|
+
if (!await pathExists(path2__default.default.join(cacheDir, "node_modules", name))) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
async function installFrameworkCache(cacheDir, packages) {
|
|
265
|
+
await fs2__default.default.mkdir(cacheDir, { recursive: true });
|
|
266
|
+
await fs2__default.default.writeFile(path2__default.default.join(cacheDir, "package.json"), JSON.stringify({
|
|
267
|
+
private: true,
|
|
268
|
+
type: "module",
|
|
269
|
+
dependencies: Object.fromEntries(packages.map((pkg) => packageToDependency(pkg)))
|
|
270
|
+
}, null, 2));
|
|
271
|
+
await runInstall(cacheDir);
|
|
272
|
+
}
|
|
273
|
+
function runInstall(cwd) {
|
|
274
|
+
return new Promise((resolve, reject) => {
|
|
275
|
+
const child = child_process.spawn("npm", ["install", "--legacy-peer-deps", "--no-audit", "--no-fund", "--loglevel=notice"], {
|
|
276
|
+
cwd,
|
|
277
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
278
|
+
});
|
|
279
|
+
const progress = createProgress("vitepad: installing framework packages");
|
|
280
|
+
let output = "";
|
|
281
|
+
child.stdout.on("data", (chunk) => {
|
|
282
|
+
output += chunk;
|
|
283
|
+
progress.tick();
|
|
284
|
+
});
|
|
285
|
+
child.stderr.on("data", (chunk) => {
|
|
286
|
+
output += chunk;
|
|
287
|
+
progress.tick();
|
|
288
|
+
});
|
|
289
|
+
child.on("error", (error) => {
|
|
290
|
+
progress.stop();
|
|
291
|
+
reject(error);
|
|
292
|
+
});
|
|
293
|
+
child.on("exit", (code) => {
|
|
294
|
+
progress.stop();
|
|
295
|
+
if (code === 0) {
|
|
296
|
+
log.done("install complete");
|
|
297
|
+
resolve();
|
|
298
|
+
} else {
|
|
299
|
+
reject(new Error(`npm install failed with exit code ${code}
|
|
300
|
+
${output.trim()}`));
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async function linkPeerPackages(cacheDir) {
|
|
306
|
+
await fs2__default.default.mkdir(path2__default.default.join(cacheDir, "node_modules"), { recursive: true });
|
|
307
|
+
for (const packageName of linkedPeerPackages) {
|
|
308
|
+
try {
|
|
309
|
+
await linkPackage({
|
|
310
|
+
source: path2__default.default.join(packageNodeModules(), packageName),
|
|
311
|
+
target: path2__default.default.join(cacheDir, "node_modules", packageName)
|
|
312
|
+
});
|
|
313
|
+
} catch (error) {
|
|
314
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
315
|
+
throw new Error(`Failed to link ${packageName} into vitepad cache at ${cacheDir}.
|
|
316
|
+
${message}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async function linkPackage(input) {
|
|
321
|
+
const source = await fs2__default.default.realpath(input.source);
|
|
322
|
+
const existing = await fs2__default.default.lstat(input.target).catch(() => null);
|
|
323
|
+
if (existing) {
|
|
324
|
+
if (existing.isSymbolicLink()) {
|
|
325
|
+
const current = await fs2__default.default.realpath(input.target).catch(() => null);
|
|
326
|
+
if (current === source) return;
|
|
327
|
+
}
|
|
328
|
+
await fs2__default.default.rm(input.target, { recursive: true, force: true });
|
|
329
|
+
}
|
|
330
|
+
await fs2__default.default.symlink(source, input.target, "dir");
|
|
331
|
+
}
|
|
332
|
+
function createProgress(label) {
|
|
333
|
+
const width = 18;
|
|
334
|
+
let frame = 0;
|
|
335
|
+
const render = () => {
|
|
336
|
+
const position = frame % width;
|
|
337
|
+
const bar = Array.from({ length: width }, (_, index) => index === position ? "=" : "-").join("");
|
|
338
|
+
process.stderr.write(`\r${label} [${bar}]`);
|
|
339
|
+
frame += 1;
|
|
340
|
+
};
|
|
341
|
+
const timer = setInterval(render, 120);
|
|
342
|
+
render();
|
|
343
|
+
return {
|
|
344
|
+
tick: render,
|
|
345
|
+
stop() {
|
|
346
|
+
clearInterval(timer);
|
|
347
|
+
process.stderr.write(`\r${" ".repeat(label.length + width + 4)}\r`);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function packageToDependency(pkg) {
|
|
352
|
+
const parsed = splitPackageSpec(pkg);
|
|
353
|
+
return [parsed.name, parsed.version];
|
|
354
|
+
}
|
|
355
|
+
function splitPackageSpec(spec) {
|
|
356
|
+
if (spec.startsWith("@")) {
|
|
357
|
+
const secondAt = spec.indexOf("@", 1);
|
|
358
|
+
if (secondAt === -1) return { name: spec, version: "latest" };
|
|
359
|
+
return { name: spec.slice(0, secondAt), version: spec.slice(secondAt + 1) };
|
|
360
|
+
}
|
|
361
|
+
const at = spec.indexOf("@");
|
|
362
|
+
if (at === -1) return { name: spec, version: "latest" };
|
|
363
|
+
return { name: spec.slice(0, at), version: spec.slice(at + 1) };
|
|
364
|
+
}
|
|
365
|
+
async function importCachePackage(cacheDir, packageName) {
|
|
366
|
+
const requireFromCache = module$1.createRequire(path2__default.default.join(cacheDir, "package.json"));
|
|
367
|
+
const resolved = requireFromCache.resolve(packageName);
|
|
368
|
+
return import(url.pathToFileURL(resolved).href);
|
|
369
|
+
}
|
|
370
|
+
async function pathExists(file) {
|
|
371
|
+
return fs2__default.default.access(file).then(() => true, () => false);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/runtime/index.ts
|
|
375
|
+
var rootDir = path2__default.default.resolve(url.fileURLToPath(new URL("..", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))));
|
|
376
|
+
var supportedComponentExts = /* @__PURE__ */ new Set([".jsx", ".tsx", ".vue", ".svelte"]);
|
|
377
|
+
var supportedMainExts = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"]);
|
|
378
|
+
var frameworkValues = /* @__PURE__ */ new Set(["auto", "react", "preact", "solid", "vue", "svelte", "vanilla"]);
|
|
379
|
+
async function run(argv) {
|
|
380
|
+
const options = parseArgs(argv);
|
|
381
|
+
if (options.help) {
|
|
382
|
+
console.log(helpText());
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (!options.entry) {
|
|
386
|
+
throw new Error(helpText("Missing entry file."));
|
|
387
|
+
}
|
|
388
|
+
const entry = path2__default.default.resolve(process2__default.default.cwd(), options.entry);
|
|
389
|
+
const stat = await fs2__default.default.stat(entry).catch(() => null);
|
|
390
|
+
if (!stat?.isFile()) {
|
|
391
|
+
throw new Error(`Entry file does not exist: ${entry}`);
|
|
392
|
+
}
|
|
393
|
+
const extension = path2__default.default.extname(entry).toLowerCase();
|
|
394
|
+
const mode = inferMode(extension);
|
|
395
|
+
const source = await fs2__default.default.readFile(entry, "utf8");
|
|
396
|
+
const framework = inferFramework({
|
|
397
|
+
extension,
|
|
398
|
+
source,
|
|
399
|
+
requested: options.framework,
|
|
400
|
+
version: options.frameworkVersion
|
|
401
|
+
});
|
|
402
|
+
validateCombination({ mode, framework: framework.name, extension });
|
|
403
|
+
const resolvedFramework = await resolveFramework(framework, { forceInstall: options.forceInstall });
|
|
404
|
+
const classTokens = await collectClassTokens(entry);
|
|
405
|
+
const workspace = await createWorkspace({
|
|
406
|
+
entry,
|
|
407
|
+
mode,
|
|
408
|
+
framework: resolvedFramework.name,
|
|
409
|
+
sourceDirs: uniquePaths([process2__default.default.cwd(), path2__default.default.dirname(entry)]),
|
|
410
|
+
classTokens,
|
|
411
|
+
packageLinks: resolvedFramework.packageLinks
|
|
412
|
+
});
|
|
413
|
+
const config = await loadUserConfig(options.config);
|
|
414
|
+
const server = await vite.createServer(vite.mergeConfig({
|
|
415
|
+
root: workspace,
|
|
416
|
+
configFile: false,
|
|
417
|
+
server: {
|
|
418
|
+
host: options.host,
|
|
419
|
+
port: options.port,
|
|
420
|
+
open: options.open,
|
|
421
|
+
watch: {
|
|
422
|
+
ignored: (file) => vite.normalizePath(file).startsWith(`${vite.normalizePath(workspace)}/`)
|
|
423
|
+
},
|
|
424
|
+
fs: {
|
|
425
|
+
allow: [
|
|
426
|
+
rootDir,
|
|
427
|
+
process2__default.default.cwd(),
|
|
428
|
+
path2__default.default.dirname(entry),
|
|
429
|
+
workspace,
|
|
430
|
+
...resolvedFramework.cacheDir ? [resolvedFramework.cacheDir] : []
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
plugins: await loadPlugins(resolvedFramework),
|
|
435
|
+
resolve: {
|
|
436
|
+
alias: [
|
|
437
|
+
...resolvedFramework.aliases,
|
|
438
|
+
{
|
|
439
|
+
find: "@",
|
|
440
|
+
replacement: path2__default.default.dirname(entry)
|
|
441
|
+
}
|
|
442
|
+
],
|
|
443
|
+
dedupe: frameworkDedupe(resolvedFramework.name)
|
|
444
|
+
},
|
|
445
|
+
optimizeDeps: {
|
|
446
|
+
entries: [path2__default.default.join(workspace, "src/main.js")],
|
|
447
|
+
include: frameworkOptimizeDeps(resolvedFramework.name)
|
|
448
|
+
},
|
|
449
|
+
customLogger: createVitepadLogger()
|
|
450
|
+
}, config));
|
|
451
|
+
await server.listen();
|
|
452
|
+
printReady({
|
|
453
|
+
entry,
|
|
454
|
+
framework: resolvedFramework,
|
|
455
|
+
urls: server.resolvedUrls
|
|
456
|
+
});
|
|
457
|
+
const close = async () => {
|
|
458
|
+
await server.close();
|
|
459
|
+
process2__default.default.exit(0);
|
|
460
|
+
};
|
|
461
|
+
process2__default.default.once("SIGINT", close);
|
|
462
|
+
process2__default.default.once("SIGTERM", close);
|
|
463
|
+
}
|
|
464
|
+
function parseArgs(argv) {
|
|
465
|
+
const options = {
|
|
466
|
+
framework: "auto",
|
|
467
|
+
frameworkVersion: "latest",
|
|
468
|
+
forceInstall: false,
|
|
469
|
+
port: 8e3,
|
|
470
|
+
host: "0.0.0.0",
|
|
471
|
+
open: "/",
|
|
472
|
+
help: false
|
|
473
|
+
};
|
|
474
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
475
|
+
const arg = argv[index];
|
|
476
|
+
if (arg === "-h" || arg === "--help") {
|
|
477
|
+
options.help = true;
|
|
478
|
+
} else if (arg === "--framework" || arg === "-f") {
|
|
479
|
+
Object.assign(options, parseFramework(readValue(argv, ++index, arg)));
|
|
480
|
+
} else if (arg.startsWith("--framework=")) {
|
|
481
|
+
Object.assign(options, parseFramework(arg.slice("--framework=".length)));
|
|
482
|
+
} else if (arg === "--force-install") {
|
|
483
|
+
options.forceInstall = true;
|
|
484
|
+
} else if (arg === "--port" || arg === "-p") {
|
|
485
|
+
options.port = Number(readValue(argv, ++index, arg));
|
|
486
|
+
} else if (arg.startsWith("--port=")) {
|
|
487
|
+
options.port = Number(arg.slice("--port=".length));
|
|
488
|
+
} else if (arg === "--host") {
|
|
489
|
+
options.host = readValue(argv, ++index, arg);
|
|
490
|
+
} else if (arg.startsWith("--host=")) {
|
|
491
|
+
options.host = arg.slice("--host=".length);
|
|
492
|
+
} else if (arg === "--no-open") {
|
|
493
|
+
options.open = false;
|
|
494
|
+
} else if (arg === "--open") {
|
|
495
|
+
options.open = "/";
|
|
496
|
+
} else if (arg === "--config" || arg === "-c") {
|
|
497
|
+
options.config = readValue(argv, ++index, arg);
|
|
498
|
+
} else if (arg.startsWith("--config=")) {
|
|
499
|
+
options.config = arg.slice("--config=".length);
|
|
500
|
+
} else if (arg.startsWith("-")) {
|
|
501
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
502
|
+
} else if (!options.entry) {
|
|
503
|
+
options.entry = arg;
|
|
504
|
+
} else {
|
|
505
|
+
throw new Error(`Unexpected argument: ${arg}`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if (!Number.isInteger(options.port) || options.port < 0 || options.port > 65535) {
|
|
509
|
+
throw new Error(`Invalid port: ${options.port}`);
|
|
510
|
+
}
|
|
511
|
+
return options;
|
|
512
|
+
}
|
|
513
|
+
function parseFramework(value) {
|
|
514
|
+
const match = value.match(/^([^@]+)(?:@(.+))?$/);
|
|
515
|
+
const name = match?.[1];
|
|
516
|
+
const version = match?.[2] || "latest";
|
|
517
|
+
if (name && frameworkValues.has(name)) {
|
|
518
|
+
return { framework: name, frameworkVersion: version };
|
|
519
|
+
}
|
|
520
|
+
throw new Error(`Unsupported framework "${value}". Use one of: ${[...frameworkValues].join(", ")}, optionally with @version.`);
|
|
521
|
+
}
|
|
522
|
+
function readValue(argv, index, option) {
|
|
523
|
+
const value = argv[index];
|
|
524
|
+
if (!value || value.startsWith("-")) {
|
|
525
|
+
throw new Error(`Missing value for ${option}`);
|
|
526
|
+
}
|
|
527
|
+
return value;
|
|
528
|
+
}
|
|
529
|
+
function inferMode(extension) {
|
|
530
|
+
if (supportedMainExts.has(extension)) return "main";
|
|
531
|
+
if (supportedComponentExts.has(extension)) return "component";
|
|
532
|
+
throw new Error(`Unsupported entry extension "${extension}". Supported: js, ts, jsx, tsx, vue, svelte.`);
|
|
533
|
+
}
|
|
534
|
+
function inferFramework(input) {
|
|
535
|
+
const { extension, source, requested, version } = input;
|
|
536
|
+
if (requested !== "auto") return { name: requested, version };
|
|
537
|
+
if (extension === ".vue") return { name: "vue", version };
|
|
538
|
+
if (extension === ".svelte") return { name: "svelte", version };
|
|
539
|
+
if (/\bfrom\s+['"]solid-js\b|\bfrom\s+['"]solid-js\/web\b|\bsolid-js\b/.test(source)) return { name: "solid", version };
|
|
540
|
+
if (/\bfrom\s+['"]preact\b|\bfrom\s+['"]preact\/compat\b|\bfrom\s+['"]preact\/hooks\b/.test(source)) return { name: "preact", version };
|
|
541
|
+
if (/\bfrom\s+['"]vue\b/.test(source)) return { name: "vue", version };
|
|
542
|
+
if (/\bfrom\s+['"]svelte\b|\bfrom\s+['"]svelte\//.test(source)) return { name: "svelte", version };
|
|
543
|
+
if (extension === ".jsx" || extension === ".tsx") return { name: "react", version };
|
|
544
|
+
return { name: "vanilla", version: "local" };
|
|
545
|
+
}
|
|
546
|
+
function validateCombination(input) {
|
|
547
|
+
const { mode, framework, extension } = input;
|
|
548
|
+
if (mode === "component" && framework === "vanilla") {
|
|
549
|
+
throw new Error(`Component entry ${extension} needs a framework. Try --framework react, vue, svelte, preact, or solid.`);
|
|
550
|
+
}
|
|
551
|
+
if (extension === ".vue" && framework !== "vue") {
|
|
552
|
+
throw new Error(".vue entries must use --framework vue.");
|
|
553
|
+
}
|
|
554
|
+
if (extension === ".svelte" && framework !== "svelte") {
|
|
555
|
+
throw new Error(".svelte entries must use --framework svelte.");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async function createWorkspace(input) {
|
|
559
|
+
const workspace = await fs2__default.default.mkdtemp(path2__default.default.join(await fs2__default.default.realpath(os__default.default.tmpdir()), "vitepad-"));
|
|
560
|
+
const srcDir = path2__default.default.join(workspace, "src");
|
|
561
|
+
const nodeModulesDir = path2__default.default.join(workspace, "node_modules");
|
|
562
|
+
await fs2__default.default.mkdir(srcDir, { recursive: true });
|
|
563
|
+
await fs2__default.default.mkdir(nodeModulesDir, { recursive: true });
|
|
564
|
+
await Promise.all([
|
|
565
|
+
fs2__default.default.writeFile(path2__default.default.join(workspace, "index.html"), htmlTemplate()),
|
|
566
|
+
fs2__default.default.writeFile(path2__default.default.join(srcDir, "style.css"), styleTemplate(input.sourceDirs, input.classTokens)),
|
|
567
|
+
fs2__default.default.writeFile(path2__default.default.join(srcDir, "main.js"), mainTemplate(input)),
|
|
568
|
+
...input.packageLinks.map((link) => linkWorkspacePackage(nodeModulesDir, link))
|
|
569
|
+
]);
|
|
570
|
+
return workspace;
|
|
571
|
+
}
|
|
572
|
+
async function linkWorkspacePackage(nodeModulesDir, link) {
|
|
573
|
+
const source = await fs2__default.default.realpath(link.source);
|
|
574
|
+
const target = path2__default.default.join(nodeModulesDir, link.name);
|
|
575
|
+
await fs2__default.default.mkdir(path2__default.default.dirname(target), { recursive: true });
|
|
576
|
+
await fs2__default.default.symlink(source, target, "dir");
|
|
577
|
+
}
|
|
578
|
+
function uniquePaths(paths) {
|
|
579
|
+
return [...new Set(paths.map((item) => vite.normalizePath(path2__default.default.resolve(item))))];
|
|
580
|
+
}
|
|
581
|
+
function styleTemplate(sourceDirs, classTokens) {
|
|
582
|
+
return [
|
|
583
|
+
'@import "tailwindcss";',
|
|
584
|
+
...sourceDirs.map((sourceDir) => `@source ${JSON.stringify(sourceDir)};`),
|
|
585
|
+
classTokens.length ? `/* vitepad safelist: ${classTokens.join(" ")} */` : "",
|
|
586
|
+
""
|
|
587
|
+
].join("\n");
|
|
588
|
+
}
|
|
589
|
+
async function collectClassTokens(entry) {
|
|
590
|
+
const visited = /* @__PURE__ */ new Set();
|
|
591
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
592
|
+
await collectFileClassTokens(entry, visited, tokens);
|
|
593
|
+
return [...tokens].sort();
|
|
594
|
+
}
|
|
595
|
+
async function collectFileClassTokens(file, visited, tokens) {
|
|
596
|
+
const resolved = path2__default.default.resolve(file);
|
|
597
|
+
if (visited.has(resolved)) return;
|
|
598
|
+
visited.add(resolved);
|
|
599
|
+
const source = await fs2__default.default.readFile(resolved, "utf8").catch(() => "");
|
|
600
|
+
for (const match of source.matchAll(/\b(?:class|className)\s*=\s*(?:"([^"]+)"|'([^']+)'|{`([^`]+)`})/g)) {
|
|
601
|
+
const value = match[1] || match[2] || match[3] || "";
|
|
602
|
+
for (const token of value.split(/\s+/)) {
|
|
603
|
+
if (token && !/[${}]/.test(token)) tokens.add(token);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
for (const specifier of localImportSpecifiers(source)) {
|
|
607
|
+
const imported = await resolveLocalImport(resolved, specifier);
|
|
608
|
+
if (imported) {
|
|
609
|
+
await collectFileClassTokens(imported, visited, tokens);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function localImportSpecifiers(source) {
|
|
614
|
+
const specifiers = /* @__PURE__ */ new Set();
|
|
615
|
+
for (const match of source.matchAll(/\bimport\s+(?:[^'"]+?\s+from\s+)?['"]([^'"]+)['"]/g)) {
|
|
616
|
+
if (isLocalSpecifier(match[1])) specifiers.add(match[1]);
|
|
617
|
+
}
|
|
618
|
+
for (const match of source.matchAll(/\bexport\s+[^'"]+?\s+from\s+['"]([^'"]+)['"]/g)) {
|
|
619
|
+
if (isLocalSpecifier(match[1])) specifiers.add(match[1]);
|
|
620
|
+
}
|
|
621
|
+
return [...specifiers];
|
|
622
|
+
}
|
|
623
|
+
function isLocalSpecifier(specifier) {
|
|
624
|
+
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
625
|
+
}
|
|
626
|
+
async function resolveLocalImport(importer, specifier) {
|
|
627
|
+
const base = path2__default.default.resolve(path2__default.default.dirname(importer), specifier);
|
|
628
|
+
const candidates = [
|
|
629
|
+
base,
|
|
630
|
+
...[".ts", ".tsx", ".js", ".jsx", ".vue", ".svelte"].map((ext) => `${base}${ext}`),
|
|
631
|
+
...["index.ts", "index.tsx", "index.js", "index.jsx", "index.vue", "index.svelte"].map((file) => path2__default.default.join(base, file))
|
|
632
|
+
];
|
|
633
|
+
for (const candidate of candidates) {
|
|
634
|
+
const stat = await fs2__default.default.stat(candidate).catch(() => null);
|
|
635
|
+
if (stat?.isFile()) return candidate;
|
|
636
|
+
}
|
|
637
|
+
return void 0;
|
|
638
|
+
}
|
|
639
|
+
function htmlTemplate() {
|
|
640
|
+
return `<!doctype html>
|
|
641
|
+
<html lang="en">
|
|
642
|
+
<head>
|
|
643
|
+
<meta charset="UTF-8" />
|
|
644
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
645
|
+
<title>vitepad</title>
|
|
646
|
+
</head>
|
|
647
|
+
<body>
|
|
648
|
+
<div id="root"></div>
|
|
649
|
+
<script type="module" src="/src/main.js"></script>
|
|
650
|
+
</body>
|
|
651
|
+
</html>
|
|
652
|
+
`;
|
|
653
|
+
}
|
|
654
|
+
function mainTemplate(input) {
|
|
655
|
+
const { entry, mode, framework } = input;
|
|
656
|
+
const importPath = `/@fs/${vite.normalizePath(entry)}`;
|
|
657
|
+
if (mode === "main") {
|
|
658
|
+
return `import './style.css'
|
|
659
|
+
import ${JSON.stringify(importPath)}
|
|
660
|
+
`;
|
|
661
|
+
}
|
|
662
|
+
switch (framework) {
|
|
663
|
+
case "react":
|
|
664
|
+
return `import './style.css'
|
|
665
|
+
import React from 'react'
|
|
666
|
+
import { createRoot } from 'react-dom/client'
|
|
667
|
+
import App from ${JSON.stringify(importPath)}
|
|
668
|
+
|
|
669
|
+
createRoot(document.getElementById('root')).render(React.createElement(App))
|
|
670
|
+
`;
|
|
671
|
+
case "preact":
|
|
672
|
+
return `import './style.css'
|
|
673
|
+
import { h, render } from 'preact'
|
|
674
|
+
import App from ${JSON.stringify(importPath)}
|
|
675
|
+
|
|
676
|
+
render(h(App, null), document.getElementById('root'))
|
|
677
|
+
`;
|
|
678
|
+
case "solid":
|
|
679
|
+
return `import './style.css'
|
|
680
|
+
import { render } from 'solid-js/web'
|
|
681
|
+
import App from ${JSON.stringify(importPath)}
|
|
682
|
+
|
|
683
|
+
render(() => App({}), document.getElementById('root'))
|
|
684
|
+
`;
|
|
685
|
+
case "vue":
|
|
686
|
+
return `import './style.css'
|
|
687
|
+
import { createApp } from 'vue'
|
|
688
|
+
import App from ${JSON.stringify(importPath)}
|
|
689
|
+
|
|
690
|
+
createApp(App).mount('#root')
|
|
691
|
+
`;
|
|
692
|
+
case "svelte":
|
|
693
|
+
return `import './style.css'
|
|
694
|
+
import { mount } from 'svelte'
|
|
695
|
+
import App from ${JSON.stringify(importPath)}
|
|
696
|
+
|
|
697
|
+
const target = document.getElementById('root')
|
|
698
|
+
const app = mount(App, { target })
|
|
699
|
+
export default app
|
|
700
|
+
`;
|
|
701
|
+
case "vanilla":
|
|
702
|
+
throw new Error("Vanilla entries cannot be used as component entries.");
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
async function loadUserConfig(configFile) {
|
|
706
|
+
if (!configFile) return {};
|
|
707
|
+
const resolved = path2__default.default.resolve(process2__default.default.cwd(), configFile);
|
|
708
|
+
const configModule = await import(url.pathToFileURL(resolved).href);
|
|
709
|
+
return configModule.default ?? configModule;
|
|
710
|
+
}
|
|
711
|
+
async function loadPlugins(framework) {
|
|
712
|
+
const [{ default: tailwindcss }, frameworkPlugins] = await Promise.all([
|
|
713
|
+
import('@tailwindcss/vite'),
|
|
714
|
+
loadFrameworkPlugins(framework)
|
|
715
|
+
]);
|
|
716
|
+
const plugins = [];
|
|
717
|
+
appendPlugin(plugins, tailwindcss());
|
|
718
|
+
plugins.push(...frameworkPlugins);
|
|
719
|
+
return plugins;
|
|
720
|
+
}
|
|
721
|
+
function printReady(input) {
|
|
722
|
+
const { entry, framework, urls } = input;
|
|
723
|
+
const frameworkLabel = `${framework.name}@${framework.version}`;
|
|
724
|
+
console.log(`${pc2__default.default.cyan("vitepad")} ${pc2__default.default.green("ready")} ${pc2__default.default.green(frameworkLabel)}`);
|
|
725
|
+
console.log(` ${pc2__default.default.gray("entry")} ${pc2__default.default.gray(entry)}`);
|
|
726
|
+
if (framework.cacheDir) {
|
|
727
|
+
console.log(` ${pc2__default.default.gray("cache")} ${pc2__default.default.gray(framework.cacheDir)}`);
|
|
728
|
+
}
|
|
729
|
+
printUrls(urls);
|
|
730
|
+
}
|
|
731
|
+
function printUrls(urls) {
|
|
732
|
+
if (!urls) return;
|
|
733
|
+
for (const url of urls.local) {
|
|
734
|
+
console.log(` ${pc2__default.default.green("\u279C")} ${pc2__default.default.bold("Local:")} ${pc2__default.default.bold(url)}`);
|
|
735
|
+
}
|
|
736
|
+
for (const url of urls.network) {
|
|
737
|
+
console.log(` ${pc2__default.default.green("\u279C")} ${pc2__default.default.bold("Network:")} ${pc2__default.default.bold(url)}`);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function createVitepadLogger() {
|
|
741
|
+
const logger = vite.createLogger("info");
|
|
742
|
+
const info = logger.info.bind(logger);
|
|
743
|
+
return {
|
|
744
|
+
...logger,
|
|
745
|
+
info(message, options) {
|
|
746
|
+
if (isNoisyViteInfo(message)) return;
|
|
747
|
+
info(message, options);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
function isNoisyViteInfo(message) {
|
|
752
|
+
return /\[vite\]\s+\(client\)\s+(hmr update|page reload)\b/.test(stripAnsi(message));
|
|
753
|
+
}
|
|
754
|
+
function stripAnsi(value) {
|
|
755
|
+
return value.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
756
|
+
}
|
|
757
|
+
function helpText(prefix) {
|
|
758
|
+
return `${prefix ? `${prefix}
|
|
759
|
+
|
|
760
|
+
` : ""}Usage:
|
|
761
|
+
vitepad <entry> [options]
|
|
762
|
+
|
|
763
|
+
Entries:
|
|
764
|
+
.js, .ts Treated as main entry files.
|
|
765
|
+
.jsx, .tsx, .vue, .svelte Treated as App component files.
|
|
766
|
+
|
|
767
|
+
Options:
|
|
768
|
+
-f, --framework <name> auto, react, preact, solid, vue, svelte, vanilla
|
|
769
|
+
Version specs are supported, e.g. react@18, vue@3.4.
|
|
770
|
+
--force-install Reinstall the selected framework cache.
|
|
771
|
+
-p, --port <number> Dev server port. Default: 8000
|
|
772
|
+
--host <host> Dev server host. Default: 0.0.0.0
|
|
773
|
+
--no-open Do not open the browser automatically.
|
|
774
|
+
-c, --config <file> Merge an extra Vite config file.
|
|
775
|
+
-h, --help Show help.
|
|
776
|
+
`;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
exports.parseArgs = parseArgs;
|
|
780
|
+
exports.run = run;
|
|
781
|
+
//# sourceMappingURL=index.cjs.map
|
|
782
|
+
//# sourceMappingURL=index.cjs.map
|