@simplysm/sd-cli 13.0.68 → 13.0.70
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 -957
- package/dist/builders/BaseBuilder.d.ts +23 -23
- package/dist/builders/BaseBuilder.d.ts.map +1 -1
- package/dist/builders/BaseBuilder.js +15 -15
- package/dist/builders/DtsBuilder.d.ts +4 -4
- package/dist/builders/DtsBuilder.js +1 -1
- package/dist/builders/LibraryBuilder.d.ts +3 -3
- package/dist/builders/types.d.ts +10 -10
- package/dist/capacitor/capacitor.d.ts +36 -36
- package/dist/capacitor/capacitor.js +63 -63
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/add-client.d.ts +8 -8
- package/dist/commands/add-client.js +15 -15
- package/dist/commands/add-client.js.map +1 -1
- package/dist/commands/add-server.d.ts +9 -9
- package/dist/commands/add-server.js +13 -13
- package/dist/commands/add-server.js.map +1 -1
- package/dist/commands/build.d.ts +9 -9
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/dev.d.ts +9 -9
- package/dist/commands/device.d.ts +9 -9
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +17 -17
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/init.d.ts +6 -6
- package/dist/commands/init.js +12 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +23 -23
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +25 -25
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts +13 -13
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +61 -61
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/replace-deps.d.ts +3 -3
- package/dist/commands/replace-deps.d.ts.map +1 -1
- package/dist/commands/replace-deps.js +1 -1
- package/dist/commands/replace-deps.js.map +1 -1
- package/dist/commands/typecheck.d.ts +20 -20
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +20 -20
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/commands/watch.d.ts +7 -7
- package/dist/electron/electron.d.ts +27 -27
- package/dist/electron/electron.js +32 -32
- package/dist/electron/electron.js.map +1 -1
- package/dist/infra/ResultCollector.d.ts +9 -9
- package/dist/infra/ResultCollector.js +5 -5
- package/dist/infra/SignalHandler.d.ts +7 -7
- package/dist/infra/SignalHandler.js +4 -4
- package/dist/infra/WorkerManager.d.ts +14 -14
- package/dist/infra/WorkerManager.js +11 -11
- package/dist/orchestrators/BuildOrchestrator.d.ts +19 -19
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +26 -26
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevOrchestrator.d.ts +25 -25
- package/dist/orchestrators/DevOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevOrchestrator.js +30 -30
- package/dist/orchestrators/DevOrchestrator.js.map +1 -1
- package/dist/orchestrators/WatchOrchestrator.d.ts +13 -13
- package/dist/orchestrators/WatchOrchestrator.js +17 -17
- package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +2 -2
- package/dist/sd-cli-entry.js +38 -38
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.d.ts +2 -2
- package/dist/sd-cli.js +1 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/sd-config.types.d.ts +84 -84
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/build-env.d.ts +1 -1
- package/dist/utils/config-editor.d.ts +5 -5
- package/dist/utils/config-editor.js +2 -2
- package/dist/utils/config-editor.js.map +1 -1
- package/dist/utils/copy-public.d.ts +9 -9
- package/dist/utils/copy-src.d.ts +9 -9
- package/dist/utils/esbuild-config.d.ts +30 -30
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/output-utils.d.ts +6 -6
- package/dist/utils/package-utils.d.ts +6 -6
- package/dist/utils/package-utils.js +1 -1
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/rebuild-manager.js +3 -3
- package/dist/utils/rebuild-manager.js.map +1 -1
- package/dist/utils/replace-deps.d.ts +25 -25
- package/dist/utils/replace-deps.js +3 -3
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/sd-config.d.ts +3 -3
- package/dist/utils/sd-config.js +3 -3
- package/dist/utils/sd-config.js.map +1 -1
- package/dist/utils/tailwind-config-deps.d.ts +3 -3
- package/dist/utils/template.d.ts +8 -8
- package/dist/utils/tsconfig.d.ts +16 -16
- package/dist/utils/tsconfig.js +2 -2
- package/dist/utils/tsconfig.js.map +1 -1
- package/dist/utils/typecheck-serialization.d.ts +8 -8
- package/dist/utils/vite-config.d.ts +8 -8
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +3 -3
- package/dist/utils/worker-events.d.ts +12 -12
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-utils.d.ts +3 -3
- package/dist/utils/worker-utils.js +2 -2
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts +14 -14
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +1 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/dts.worker.d.ts +13 -13
- package/dist/workers/dts.worker.d.ts.map +1 -1
- package/dist/workers/dts.worker.js +3 -3
- package/dist/workers/dts.worker.js.map +1 -1
- package/dist/workers/library.worker.d.ts +12 -12
- package/dist/workers/library.worker.js +1 -1
- package/dist/workers/library.worker.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +1 -1
- package/dist/workers/server-runtime.worker.d.ts +6 -6
- package/dist/workers/server-runtime.worker.js +6 -6
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/dist/workers/server.worker.d.ts +20 -20
- package/dist/workers/server.worker.d.ts.map +1 -1
- package/dist/workers/server.worker.js +6 -6
- package/dist/workers/server.worker.js.map +1 -1
- package/package.json +8 -7
- package/src/builders/BaseBuilder.ts +33 -33
- package/src/builders/DtsBuilder.ts +5 -5
- package/src/builders/LibraryBuilder.ts +9 -9
- package/src/builders/types.ts +10 -10
- package/src/capacitor/capacitor.ts +119 -119
- package/src/commands/add-client.ts +31 -31
- package/src/commands/add-server.ts +34 -34
- package/src/commands/build.ts +9 -9
- package/src/commands/check.ts +5 -5
- package/src/commands/dev.ts +9 -9
- package/src/commands/device.ts +30 -30
- package/src/commands/init.ts +25 -25
- package/src/commands/lint.ts +64 -64
- package/src/commands/publish.ts +139 -139
- package/src/commands/replace-deps.ts +4 -4
- package/src/commands/typecheck.ts +74 -74
- package/src/commands/watch.ts +7 -7
- package/src/electron/electron.ts +51 -51
- package/src/infra/ResultCollector.ts +9 -9
- package/src/infra/SignalHandler.ts +7 -7
- package/src/infra/WorkerManager.ts +14 -14
- package/src/orchestrators/BuildOrchestrator.ts +76 -76
- package/src/orchestrators/DevOrchestrator.ts +88 -88
- package/src/orchestrators/WatchOrchestrator.ts +39 -39
- package/src/sd-cli-entry.ts +43 -43
- package/src/sd-cli.ts +15 -15
- package/src/sd-config.types.ts +85 -85
- package/src/utils/build-env.ts +1 -1
- package/src/utils/config-editor.ts +19 -19
- package/src/utils/copy-public.ts +17 -17
- package/src/utils/copy-src.ts +11 -11
- package/src/utils/esbuild-config.ts +33 -33
- package/src/utils/output-utils.ts +11 -11
- package/src/utils/package-utils.ts +12 -12
- package/src/utils/rebuild-manager.ts +3 -3
- package/src/utils/replace-deps.ts +361 -361
- package/src/utils/sd-config.ts +44 -44
- package/src/utils/tailwind-config-deps.ts +98 -98
- package/src/utils/template.ts +56 -56
- package/src/utils/tsconfig.ts +127 -127
- package/src/utils/typecheck-serialization.ts +86 -86
- package/src/utils/vite-config.ts +341 -341
- package/src/utils/worker-events.ts +16 -16
- package/src/utils/worker-utils.ts +45 -45
- package/src/workers/client.worker.ts +34 -34
- package/src/workers/dts.worker.ts +467 -467
- package/src/workers/library.worker.ts +314 -314
- package/src/workers/lint.worker.ts +16 -16
- package/src/workers/server-runtime.worker.ts +157 -157
- package/src/workers/server.worker.ts +572 -572
- package/templates/add-client/__CLIENT__/package.json.hbs +1 -1
- package/templates/add-server/__SERVER__/package.json.hbs +2 -2
- package/templates/init/package.json.hbs +3 -3
- package/tests/config-editor.spec.ts +160 -0
- package/tests/copy-src.spec.ts +50 -0
- package/tests/get-compiler-options-for-package.spec.ts +139 -0
- package/tests/get-package-source-files.spec.ts +181 -0
- package/tests/get-types-from-package-json.spec.ts +107 -0
- package/tests/infra/ResultCollector.spec.ts +39 -0
- package/tests/infra/SignalHandler.spec.ts +38 -0
- package/tests/infra/WorkerManager.spec.ts +97 -0
- package/tests/load-ignore-patterns.spec.ts +188 -0
- package/tests/load-sd-config.spec.ts +137 -0
- package/tests/package-utils.spec.ts +188 -0
- package/tests/parse-root-tsconfig.spec.ts +89 -0
- package/tests/replace-deps.spec.ts +308 -0
- package/tests/run-lint.spec.ts +415 -0
- package/tests/run-typecheck.spec.ts +653 -0
- package/tests/run-watch.spec.ts +75 -0
- package/tests/sd-cli.spec.ts +330 -0
- package/tests/tailwind-config-deps.spec.ts +30 -0
- package/tests/template.spec.ts +70 -0
- package/tests/utils/rebuild-manager.spec.ts +43 -0
- package/tests/write-changed-output-files.spec.ts +97 -0
package/src/utils/vite-config.ts
CHANGED
|
@@ -1,341 +1,341 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import { createRequire } from "module";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import type { Plugin, UserConfig as ViteUserConfig } from "vite";
|
|
5
|
-
import tsconfigPaths from "vite-tsconfig-paths";
|
|
6
|
-
import solidPlugin from "vite-plugin-solid";
|
|
7
|
-
import { VitePWA } from "vite-plugin-pwa";
|
|
8
|
-
import tailwindcss from "tailwindcss";
|
|
9
|
-
import type esbuild from "esbuild";
|
|
10
|
-
import { getTailwindConfigDeps } from "./tailwind-config-deps.js";
|
|
11
|
-
import { FsWatcher, pathNorm } from "@simplysm/core-node";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* Tailwind CSS
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*/
|
|
20
|
-
function sdTailwindConfigDepsPlugin(pkgDir: string, replaceDeps: string[]): Plugin {
|
|
21
|
-
return {
|
|
22
|
-
name: "sd-tailwind-config-deps",
|
|
23
|
-
configureServer(server) {
|
|
24
|
-
const configPath = path.join(pkgDir, "tailwind.config.ts");
|
|
25
|
-
if (!fs.existsSync(configPath)) return;
|
|
26
|
-
|
|
27
|
-
const allDeps = getTailwindConfigDeps(configPath, replaceDeps);
|
|
28
|
-
const configAbsolute = path.resolve(configPath);
|
|
29
|
-
const externalDeps = allDeps.filter((d) => d !== configAbsolute);
|
|
30
|
-
if (externalDeps.length === 0) return;
|
|
31
|
-
|
|
32
|
-
for (const dep of externalDeps) {
|
|
33
|
-
server.watcher.add(dep);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
server.watcher.on("change", (changed) => {
|
|
37
|
-
if (externalDeps.some((d) => pathNorm(d) === pathNorm(changed))) {
|
|
38
|
-
// jiti (Tailwind
|
|
39
|
-
//
|
|
40
|
-
const _require = createRequire(import.meta.url);
|
|
41
|
-
for (const dep of allDeps) {
|
|
42
|
-
delete _require.cache[dep];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Tailwind
|
|
46
|
-
const now = new Date();
|
|
47
|
-
fs.utimesSync(configPath, now, now);
|
|
48
|
-
server.ws.send({ type: "full-reload" });
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* 1. realpath
|
|
61
|
-
* 2.
|
|
62
|
-
*/
|
|
63
|
-
function isSubpathOnlyPackage(pkgJsonPath: string): boolean {
|
|
64
|
-
try {
|
|
65
|
-
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")) as {
|
|
66
|
-
exports?: Record<string, unknown> | string;
|
|
67
|
-
main?: string;
|
|
68
|
-
module?: string;
|
|
69
|
-
};
|
|
70
|
-
if (
|
|
71
|
-
pkgJson.exports != null &&
|
|
72
|
-
typeof pkgJson.exports === "object" &&
|
|
73
|
-
!("." in pkgJson.exports) &&
|
|
74
|
-
pkgJson.main == null &&
|
|
75
|
-
pkgJson.module == null
|
|
76
|
-
) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
} catch {
|
|
80
|
-
//
|
|
81
|
-
}
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* public-dev/
|
|
87
|
-
* Vite
|
|
88
|
-
*/
|
|
89
|
-
function sdPublicDevPlugin(pkgDir: string): Plugin {
|
|
90
|
-
const publicDevDir = path.join(pkgDir, "public-dev");
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
name: "sd-public-dev",
|
|
94
|
-
configureServer(server) {
|
|
95
|
-
if (!fs.existsSync(publicDevDir)) return;
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
server.middlewares.use((req, res, next) => {
|
|
99
|
-
if (req.url == null) {
|
|
100
|
-
next();
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// base path
|
|
105
|
-
const base = server.config.base || "/";
|
|
106
|
-
let urlPath = req.url.split("?")[0];
|
|
107
|
-
if (urlPath.startsWith(base)) {
|
|
108
|
-
urlPath = urlPath.slice(base.length);
|
|
109
|
-
}
|
|
110
|
-
if (urlPath.startsWith("/")) {
|
|
111
|
-
urlPath = urlPath.slice(1);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
const decodedPath = decodeURIComponent(urlPath);
|
|
116
|
-
const filePath = path.resolve(publicDevDir, decodedPath);
|
|
117
|
-
const normalizedRoot = path.resolve(publicDevDir);
|
|
118
|
-
if (!filePath.startsWith(normalizedRoot + path.sep) && filePath !== normalizedRoot) {
|
|
119
|
-
next();
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
124
|
-
//
|
|
125
|
-
const stream = fs.createReadStream(filePath);
|
|
126
|
-
stream.pipe(res);
|
|
127
|
-
} else {
|
|
128
|
-
next();
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* Vite
|
|
139
|
-
* scope
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* optimizeDeps
|
|
143
|
-
*/
|
|
144
|
-
function sdScopeWatchPlugin(
|
|
145
|
-
pkgDir: string,
|
|
146
|
-
replaceDeps: string[],
|
|
147
|
-
onScopeRebuild?: () => void,
|
|
148
|
-
): Plugin {
|
|
149
|
-
return {
|
|
150
|
-
name: "sd-scope-watch",
|
|
151
|
-
config() {
|
|
152
|
-
const excluded: string[] = [];
|
|
153
|
-
const nestedDepsToInclude: string[] = [];
|
|
154
|
-
|
|
155
|
-
for (const pkg of replaceDeps) {
|
|
156
|
-
excluded.push(pkg);
|
|
157
|
-
|
|
158
|
-
const pkgParts = pkg.split("/");
|
|
159
|
-
const depPkgJsonPath = path.join(pkgDir, "node_modules", ...pkgParts, "package.json");
|
|
160
|
-
try {
|
|
161
|
-
const depPkgJson = JSON.parse(fs.readFileSync(depPkgJsonPath, "utf-8")) as {
|
|
162
|
-
dependencies?: Record<string, string>;
|
|
163
|
-
};
|
|
164
|
-
for (const dep of Object.keys(depPkgJson.dependencies ?? {})) {
|
|
165
|
-
if (replaceDeps.includes(dep)) continue;
|
|
166
|
-
if (dep === "solid-js" || dep.startsWith("@solidjs/") || dep.startsWith("solid-"))
|
|
167
|
-
continue;
|
|
168
|
-
if (dep === "tailwindcss") continue;
|
|
169
|
-
|
|
170
|
-
const realPkgPath = fs.realpathSync(path.join(pkgDir, "node_modules", ...pkgParts));
|
|
171
|
-
const pnpmNodeModules = path.resolve(realPkgPath, "../..");
|
|
172
|
-
const depPkgJsonResolved = path.join(pnpmNodeModules, dep, "package.json");
|
|
173
|
-
if (isSubpathOnlyPackage(depPkgJsonResolved)) continue;
|
|
174
|
-
|
|
175
|
-
const depPkgJsonFallback = path.join(
|
|
176
|
-
pkgDir,
|
|
177
|
-
"node_modules",
|
|
178
|
-
...pkgParts,
|
|
179
|
-
"node_modules",
|
|
180
|
-
dep,
|
|
181
|
-
"package.json",
|
|
182
|
-
);
|
|
183
|
-
if (isSubpathOnlyPackage(depPkgJsonFallback)) continue;
|
|
184
|
-
|
|
185
|
-
nestedDepsToInclude.push(`${pkg} > ${dep}`);
|
|
186
|
-
}
|
|
187
|
-
} catch {
|
|
188
|
-
// package.json
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
optimizeDeps: {
|
|
194
|
-
force: true,
|
|
195
|
-
exclude: excluded,
|
|
196
|
-
include: [...new Set(nestedDepsToInclude)],
|
|
197
|
-
},
|
|
198
|
-
};
|
|
199
|
-
},
|
|
200
|
-
async configureServer(server) {
|
|
201
|
-
const watchPaths: string[] = [];
|
|
202
|
-
|
|
203
|
-
for (const pkg of replaceDeps) {
|
|
204
|
-
const pkgParts = pkg.split("/");
|
|
205
|
-
const pkgRoot = path.join(pkgDir, "node_modules", ...pkgParts);
|
|
206
|
-
if (!fs.existsSync(pkgRoot)) continue;
|
|
207
|
-
|
|
208
|
-
const distDir = path.join(pkgRoot, "dist");
|
|
209
|
-
if (fs.existsSync(distDir)) {
|
|
210
|
-
watchPaths.push(distDir);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
for (const file of fs.readdirSync(pkgRoot)) {
|
|
214
|
-
if (
|
|
215
|
-
file.endsWith(".css") ||
|
|
216
|
-
file === "tailwind.config.ts" ||
|
|
217
|
-
file === "tailwind.config.js"
|
|
218
|
-
) {
|
|
219
|
-
watchPaths.push(path.join(pkgRoot, file));
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (watchPaths.length === 0) return;
|
|
225
|
-
|
|
226
|
-
const scopeWatcher = await FsWatcher.watch(watchPaths);
|
|
227
|
-
scopeWatcher.onChange({ delay: 300 }, (changeInfos) => {
|
|
228
|
-
for (const { path: changedPath } of changeInfos) {
|
|
229
|
-
let realPath: string;
|
|
230
|
-
try {
|
|
231
|
-
realPath = fs.realpathSync(changedPath);
|
|
232
|
-
} catch {
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
server.watcher.emit("change", realPath);
|
|
236
|
-
}
|
|
237
|
-
onScopeRebuild?.();
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
server.httpServer?.on("close", () => void scopeWatcher.close());
|
|
241
|
-
},
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Vite
|
|
247
|
-
*/
|
|
248
|
-
export interface ViteConfigOptions {
|
|
249
|
-
pkgDir: string;
|
|
250
|
-
name: string;
|
|
251
|
-
tsconfigPath: string;
|
|
252
|
-
compilerOptions: Record<string, unknown>;
|
|
253
|
-
env?: Record<string, string>;
|
|
254
|
-
mode: "build" | "dev";
|
|
255
|
-
/**
|
|
256
|
-
serverPort?: number;
|
|
257
|
-
/** replaceDeps
|
|
258
|
-
replaceDeps?: string[];
|
|
259
|
-
/** replaceDeps
|
|
260
|
-
onScopeRebuild?: () => void;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Vite
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
* - build
|
|
268
|
-
* - dev
|
|
269
|
-
*/
|
|
270
|
-
export function createViteConfig(options: ViteConfigOptions): ViteUserConfig {
|
|
271
|
-
const { pkgDir, name, tsconfigPath, compilerOptions, env, mode, serverPort, replaceDeps } =
|
|
272
|
-
options;
|
|
273
|
-
|
|
274
|
-
// Read package.json to extract app name for PWA manifest
|
|
275
|
-
const pkgJsonPath = path.join(pkgDir, "package.json");
|
|
276
|
-
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")) as { name: string };
|
|
277
|
-
const appName = pkgJson.name.replace(/^@[^/]+\//, "");
|
|
278
|
-
|
|
279
|
-
//
|
|
280
|
-
const envDefine: Record<string, string> = {};
|
|
281
|
-
if (env != null) {
|
|
282
|
-
envDefine["process.env"] = JSON.stringify(env);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const config: ViteUserConfig = {
|
|
286
|
-
root: pkgDir,
|
|
287
|
-
base: `/${name}/`,
|
|
288
|
-
plugins: [
|
|
289
|
-
tsconfigPaths({ projects: [tsconfigPath] }),
|
|
290
|
-
solidPlugin(),
|
|
291
|
-
VitePWA({
|
|
292
|
-
registerType: "prompt",
|
|
293
|
-
injectRegister: "script",
|
|
294
|
-
manifest: {
|
|
295
|
-
name: appName,
|
|
296
|
-
short_name: appName,
|
|
297
|
-
display: "standalone",
|
|
298
|
-
theme_color: "#ffffff",
|
|
299
|
-
background_color: "#ffffff",
|
|
300
|
-
},
|
|
301
|
-
workbox: {
|
|
302
|
-
globPatterns: ["**/*.{js,css,html,ico,png,svg,woff2}"],
|
|
303
|
-
},
|
|
304
|
-
}),
|
|
305
|
-
...(replaceDeps != null && replaceDeps.length > 0
|
|
306
|
-
? [sdTailwindConfigDepsPlugin(pkgDir, replaceDeps)]
|
|
307
|
-
: []),
|
|
308
|
-
...(replaceDeps != null && replaceDeps.length > 0
|
|
309
|
-
? [sdScopeWatchPlugin(pkgDir, replaceDeps, options.onScopeRebuild)]
|
|
310
|
-
: []),
|
|
311
|
-
...(mode === "dev" ? [sdPublicDevPlugin(pkgDir)] : []),
|
|
312
|
-
],
|
|
313
|
-
css: {
|
|
314
|
-
postcss: {
|
|
315
|
-
plugins: [tailwindcss({ config: path.join(pkgDir, "tailwind.config.ts") })],
|
|
316
|
-
},
|
|
317
|
-
},
|
|
318
|
-
esbuild: {
|
|
319
|
-
tsconfigRaw: { compilerOptions: compilerOptions as esbuild.TsconfigRaw["compilerOptions"] },
|
|
320
|
-
},
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
//
|
|
324
|
-
config.define = envDefine;
|
|
325
|
-
|
|
326
|
-
if (mode === "build") {
|
|
327
|
-
config.logLevel = "silent";
|
|
328
|
-
} else {
|
|
329
|
-
//
|
|
330
|
-
config.server = {
|
|
331
|
-
// serverPort === 0:
|
|
332
|
-
// → host
|
|
333
|
-
// (Windows
|
|
334
|
-
host: serverPort === 0 ? "127.0.0.1" : undefined,
|
|
335
|
-
port: serverPort === 0 ? undefined : serverPort,
|
|
336
|
-
strictPort: serverPort !== 0 && serverPort !== undefined,
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return config;
|
|
341
|
-
}
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { createRequire } from "module";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import type { Plugin, UserConfig as ViteUserConfig } from "vite";
|
|
5
|
+
import tsconfigPaths from "vite-tsconfig-paths";
|
|
6
|
+
import solidPlugin from "vite-plugin-solid";
|
|
7
|
+
import { VitePWA } from "vite-plugin-pwa";
|
|
8
|
+
import tailwindcss from "tailwindcss";
|
|
9
|
+
import type esbuild from "esbuild";
|
|
10
|
+
import { getTailwindConfigDeps } from "./tailwind-config-deps.js";
|
|
11
|
+
import { FsWatcher, pathNorm } from "@simplysm/core-node";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Vite plugin that watches scope package dependencies of a Tailwind config.
|
|
15
|
+
*
|
|
16
|
+
* Tailwind CSS's built-in dependency tracking only handles relative path imports,
|
|
17
|
+
* so it cannot detect config changes in scope packages referenced via presets.
|
|
18
|
+
* This plugin watches those files and invalidates the Tailwind cache on change.
|
|
19
|
+
*/
|
|
20
|
+
function sdTailwindConfigDepsPlugin(pkgDir: string, replaceDeps: string[]): Plugin {
|
|
21
|
+
return {
|
|
22
|
+
name: "sd-tailwind-config-deps",
|
|
23
|
+
configureServer(server) {
|
|
24
|
+
const configPath = path.join(pkgDir, "tailwind.config.ts");
|
|
25
|
+
if (!fs.existsSync(configPath)) return;
|
|
26
|
+
|
|
27
|
+
const allDeps = getTailwindConfigDeps(configPath, replaceDeps);
|
|
28
|
+
const configAbsolute = path.resolve(configPath);
|
|
29
|
+
const externalDeps = allDeps.filter((d) => d !== configAbsolute);
|
|
30
|
+
if (externalDeps.length === 0) return;
|
|
31
|
+
|
|
32
|
+
for (const dep of externalDeps) {
|
|
33
|
+
server.watcher.add(dep);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
server.watcher.on("change", (changed) => {
|
|
37
|
+
if (externalDeps.some((d) => pathNorm(d) === pathNorm(changed))) {
|
|
38
|
+
// Clear require cache used by jiti (Tailwind's config loader)
|
|
39
|
+
// so changed files are re-read on config reload
|
|
40
|
+
const _require = createRequire(import.meta.url);
|
|
41
|
+
for (const dep of allDeps) {
|
|
42
|
+
delete _require.cache[dep];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Invalidate Tailwind cache: update config mtime to trigger reload
|
|
46
|
+
const now = new Date();
|
|
47
|
+
fs.utimesSync(configPath, now, now);
|
|
48
|
+
server.ws.send({ type: "full-reload" });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if a package is subpath-only export (no "." in exports field)
|
|
57
|
+
*
|
|
58
|
+
* e.g., @tiptap/pm only exports "./state", "./view" etc., so pre-bundling is not possible.
|
|
59
|
+
* Tries two paths in pnpm structure:
|
|
60
|
+
* 1. Follow realpath to find in .pnpm node_modules
|
|
61
|
+
* 2. Fallback to symlinked workspace package's node_modules
|
|
62
|
+
*/
|
|
63
|
+
function isSubpathOnlyPackage(pkgJsonPath: string): boolean {
|
|
64
|
+
try {
|
|
65
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")) as {
|
|
66
|
+
exports?: Record<string, unknown> | string;
|
|
67
|
+
main?: string;
|
|
68
|
+
module?: string;
|
|
69
|
+
};
|
|
70
|
+
if (
|
|
71
|
+
pkgJson.exports != null &&
|
|
72
|
+
typeof pkgJson.exports === "object" &&
|
|
73
|
+
!("." in pkgJson.exports) &&
|
|
74
|
+
pkgJson.main == null &&
|
|
75
|
+
pkgJson.module == null
|
|
76
|
+
) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// Return false on read failure (include in pre-bundling)
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Vite plugin that serves files from public-dev/ directory with priority over public/ in dev mode.
|
|
87
|
+
* Keeps Vite's default publicDir (public/) while giving precedence to public-dev/ files at the same path.
|
|
88
|
+
*/
|
|
89
|
+
function sdPublicDevPlugin(pkgDir: string): Plugin {
|
|
90
|
+
const publicDevDir = path.join(pkgDir, "public-dev");
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
name: "sd-public-dev",
|
|
94
|
+
configureServer(server) {
|
|
95
|
+
if (!fs.existsSync(publicDevDir)) return;
|
|
96
|
+
|
|
97
|
+
// Check public-dev/ files before Vite's default static serving
|
|
98
|
+
server.middlewares.use((req, res, next) => {
|
|
99
|
+
if (req.url == null) {
|
|
100
|
+
next();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Strip base path
|
|
105
|
+
const base = server.config.base || "/";
|
|
106
|
+
let urlPath = req.url.split("?")[0];
|
|
107
|
+
if (urlPath.startsWith(base)) {
|
|
108
|
+
urlPath = urlPath.slice(base.length);
|
|
109
|
+
}
|
|
110
|
+
if (urlPath.startsWith("/")) {
|
|
111
|
+
urlPath = urlPath.slice(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Path traversal defense: block access to files outside publicDevDir
|
|
115
|
+
const decodedPath = decodeURIComponent(urlPath);
|
|
116
|
+
const filePath = path.resolve(publicDevDir, decodedPath);
|
|
117
|
+
const normalizedRoot = path.resolve(publicDevDir);
|
|
118
|
+
if (!filePath.startsWith(normalizedRoot + path.sep) && filePath !== normalizedRoot) {
|
|
119
|
+
next();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
124
|
+
// Respond with file stream instead of sirv
|
|
125
|
+
const stream = fs.createReadStream(filePath);
|
|
126
|
+
stream.pipe(res);
|
|
127
|
+
} else {
|
|
128
|
+
next();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Vite plugin that detects changes in scope packages' dist directories.
|
|
137
|
+
*
|
|
138
|
+
* Vite excludes node_modules from watch by default, so dist file changes
|
|
139
|
+
* in scope packages do not trigger HMR/rebuild.
|
|
140
|
+
* This plugin uses a separate FsWatcher to monitor scope packages' dist directories
|
|
141
|
+
* and triggers Vite's internal HMR pipeline on change.
|
|
142
|
+
* Excludes from optimizeDeps to prevent changes being ignored due to pre-bundled cache.
|
|
143
|
+
*/
|
|
144
|
+
function sdScopeWatchPlugin(
|
|
145
|
+
pkgDir: string,
|
|
146
|
+
replaceDeps: string[],
|
|
147
|
+
onScopeRebuild?: () => void,
|
|
148
|
+
): Plugin {
|
|
149
|
+
return {
|
|
150
|
+
name: "sd-scope-watch",
|
|
151
|
+
config() {
|
|
152
|
+
const excluded: string[] = [];
|
|
153
|
+
const nestedDepsToInclude: string[] = [];
|
|
154
|
+
|
|
155
|
+
for (const pkg of replaceDeps) {
|
|
156
|
+
excluded.push(pkg);
|
|
157
|
+
|
|
158
|
+
const pkgParts = pkg.split("/");
|
|
159
|
+
const depPkgJsonPath = path.join(pkgDir, "node_modules", ...pkgParts, "package.json");
|
|
160
|
+
try {
|
|
161
|
+
const depPkgJson = JSON.parse(fs.readFileSync(depPkgJsonPath, "utf-8")) as {
|
|
162
|
+
dependencies?: Record<string, string>;
|
|
163
|
+
};
|
|
164
|
+
for (const dep of Object.keys(depPkgJson.dependencies ?? {})) {
|
|
165
|
+
if (replaceDeps.includes(dep)) continue;
|
|
166
|
+
if (dep === "solid-js" || dep.startsWith("@solidjs/") || dep.startsWith("solid-"))
|
|
167
|
+
continue;
|
|
168
|
+
if (dep === "tailwindcss") continue;
|
|
169
|
+
|
|
170
|
+
const realPkgPath = fs.realpathSync(path.join(pkgDir, "node_modules", ...pkgParts));
|
|
171
|
+
const pnpmNodeModules = path.resolve(realPkgPath, "../..");
|
|
172
|
+
const depPkgJsonResolved = path.join(pnpmNodeModules, dep, "package.json");
|
|
173
|
+
if (isSubpathOnlyPackage(depPkgJsonResolved)) continue;
|
|
174
|
+
|
|
175
|
+
const depPkgJsonFallback = path.join(
|
|
176
|
+
pkgDir,
|
|
177
|
+
"node_modules",
|
|
178
|
+
...pkgParts,
|
|
179
|
+
"node_modules",
|
|
180
|
+
dep,
|
|
181
|
+
"package.json",
|
|
182
|
+
);
|
|
183
|
+
if (isSubpathOnlyPackage(depPkgJsonFallback)) continue;
|
|
184
|
+
|
|
185
|
+
nestedDepsToInclude.push(`${pkg} > ${dep}`);
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// Skip on package.json read failure
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
optimizeDeps: {
|
|
194
|
+
force: true,
|
|
195
|
+
exclude: excluded,
|
|
196
|
+
include: [...new Set(nestedDepsToInclude)],
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
async configureServer(server) {
|
|
201
|
+
const watchPaths: string[] = [];
|
|
202
|
+
|
|
203
|
+
for (const pkg of replaceDeps) {
|
|
204
|
+
const pkgParts = pkg.split("/");
|
|
205
|
+
const pkgRoot = path.join(pkgDir, "node_modules", ...pkgParts);
|
|
206
|
+
if (!fs.existsSync(pkgRoot)) continue;
|
|
207
|
+
|
|
208
|
+
const distDir = path.join(pkgRoot, "dist");
|
|
209
|
+
if (fs.existsSync(distDir)) {
|
|
210
|
+
watchPaths.push(distDir);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (const file of fs.readdirSync(pkgRoot)) {
|
|
214
|
+
if (
|
|
215
|
+
file.endsWith(".css") ||
|
|
216
|
+
file === "tailwind.config.ts" ||
|
|
217
|
+
file === "tailwind.config.js"
|
|
218
|
+
) {
|
|
219
|
+
watchPaths.push(path.join(pkgRoot, file));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (watchPaths.length === 0) return;
|
|
225
|
+
|
|
226
|
+
const scopeWatcher = await FsWatcher.watch(watchPaths);
|
|
227
|
+
scopeWatcher.onChange({ delay: 300 }, (changeInfos) => {
|
|
228
|
+
for (const { path: changedPath } of changeInfos) {
|
|
229
|
+
let realPath: string;
|
|
230
|
+
try {
|
|
231
|
+
realPath = fs.realpathSync(changedPath);
|
|
232
|
+
} catch {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
server.watcher.emit("change", realPath);
|
|
236
|
+
}
|
|
237
|
+
onScopeRebuild?.();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
server.httpServer?.on("close", () => void scopeWatcher.close());
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Vite config generation options
|
|
247
|
+
*/
|
|
248
|
+
export interface ViteConfigOptions {
|
|
249
|
+
pkgDir: string;
|
|
250
|
+
name: string;
|
|
251
|
+
tsconfigPath: string;
|
|
252
|
+
compilerOptions: Record<string, unknown>;
|
|
253
|
+
env?: Record<string, string>;
|
|
254
|
+
mode: "build" | "dev";
|
|
255
|
+
/** Server port in dev mode (0 for auto-assign) */
|
|
256
|
+
serverPort?: number;
|
|
257
|
+
/** Array of replaceDeps package names (resolved state) */
|
|
258
|
+
replaceDeps?: string[];
|
|
259
|
+
/** Callback when replaceDeps package dist changes */
|
|
260
|
+
onScopeRebuild?: () => void;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Create Vite config
|
|
265
|
+
*
|
|
266
|
+
* Configuration for building/dev server for client packages based on SolidJS + TailwindCSS.
|
|
267
|
+
* - build mode: production build (logLevel: silent)
|
|
268
|
+
* - dev mode: dev server (env substitution via define, server configuration)
|
|
269
|
+
*/
|
|
270
|
+
export function createViteConfig(options: ViteConfigOptions): ViteUserConfig {
|
|
271
|
+
const { pkgDir, name, tsconfigPath, compilerOptions, env, mode, serverPort, replaceDeps } =
|
|
272
|
+
options;
|
|
273
|
+
|
|
274
|
+
// Read package.json to extract app name for PWA manifest
|
|
275
|
+
const pkgJsonPath = path.join(pkgDir, "package.json");
|
|
276
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8")) as { name: string };
|
|
277
|
+
const appName = pkgJson.name.replace(/^@[^/]+\//, "");
|
|
278
|
+
|
|
279
|
+
// Process.env substitution (applied to both build and dev modes)
|
|
280
|
+
const envDefine: Record<string, string> = {};
|
|
281
|
+
if (env != null) {
|
|
282
|
+
envDefine["process.env"] = JSON.stringify(env);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const config: ViteUserConfig = {
|
|
286
|
+
root: pkgDir,
|
|
287
|
+
base: `/${name}/`,
|
|
288
|
+
plugins: [
|
|
289
|
+
tsconfigPaths({ projects: [tsconfigPath] }),
|
|
290
|
+
solidPlugin(),
|
|
291
|
+
VitePWA({
|
|
292
|
+
registerType: "prompt",
|
|
293
|
+
injectRegister: "script",
|
|
294
|
+
manifest: {
|
|
295
|
+
name: appName,
|
|
296
|
+
short_name: appName,
|
|
297
|
+
display: "standalone",
|
|
298
|
+
theme_color: "#ffffff",
|
|
299
|
+
background_color: "#ffffff",
|
|
300
|
+
},
|
|
301
|
+
workbox: {
|
|
302
|
+
globPatterns: ["**/*.{js,css,html,ico,png,svg,woff2}"],
|
|
303
|
+
},
|
|
304
|
+
}),
|
|
305
|
+
...(replaceDeps != null && replaceDeps.length > 0
|
|
306
|
+
? [sdTailwindConfigDepsPlugin(pkgDir, replaceDeps)]
|
|
307
|
+
: []),
|
|
308
|
+
...(replaceDeps != null && replaceDeps.length > 0
|
|
309
|
+
? [sdScopeWatchPlugin(pkgDir, replaceDeps, options.onScopeRebuild)]
|
|
310
|
+
: []),
|
|
311
|
+
...(mode === "dev" ? [sdPublicDevPlugin(pkgDir)] : []),
|
|
312
|
+
],
|
|
313
|
+
css: {
|
|
314
|
+
postcss: {
|
|
315
|
+
plugins: [tailwindcss({ config: path.join(pkgDir, "tailwind.config.ts") })],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
esbuild: {
|
|
319
|
+
tsconfigRaw: { compilerOptions: compilerOptions as esbuild.TsconfigRaw["compilerOptions"] },
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// Process.env substitution (applied to both build and dev modes)
|
|
324
|
+
config.define = envDefine;
|
|
325
|
+
|
|
326
|
+
if (mode === "build") {
|
|
327
|
+
config.logLevel = "silent";
|
|
328
|
+
} else {
|
|
329
|
+
// Dev mode
|
|
330
|
+
config.server = {
|
|
331
|
+
// serverPort === 0: server-connected client (proxy target)
|
|
332
|
+
// → explicitly set host to 127.0.0.1 to ensure IPv4 binding
|
|
333
|
+
// (On Windows, if localhost resolves to ::1 (IPv6), proxy connection to 127.0.0.1 causes ECONNREFUSED)
|
|
334
|
+
host: serverPort === 0 ? "127.0.0.1" : undefined,
|
|
335
|
+
port: serverPort === 0 ? undefined : serverPort,
|
|
336
|
+
strictPort: serverPort !== 0 && serverPort !== undefined,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return config;
|
|
341
|
+
}
|