@stacksjs/actions 0.70.160 → 0.70.162
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 +1 -1
- package/dist/blog.js +1 -1
- package/dist/build/desktop.js +58 -4
- package/dist/build/views.js +11 -6
- package/dist/build.d.ts +1 -1
- package/dist/build.js +2 -2
- package/dist/copy-types.js +0 -1
- package/dist/dev/components.js +1 -1
- package/dist/dev/dashboard.js +8 -8
- package/dist/dev/desktop.d.ts +1 -1
- package/dist/dev/desktop.js +1 -5
- package/dist/dev/system-tray.js +171 -4
- package/dist/examples.js +2 -2
- package/dist/helpers/lib-entries.js +3 -3
- package/dist/helpers/vscode-custom-data.d.ts +1 -0
- package/dist/helpers/vscode-custom-data.js +32 -2
- package/dist/make.d.ts +2 -0
- package/dist/make.js +10 -4
- package/dist/stacks.d.ts +1 -1
- package/dist/stacks.js +208 -157
- package/dist/templates.js +11 -8
- package/dist/test/feature.js +2 -11
- package/dist/test/index.js +2 -11
- package/dist/test/runner.d.ts +1 -0
- package/dist/test/runner.js +30 -0
- package/dist/test/unit.js +2 -11
- package/dist/upgrade/packages.d.ts +18 -0
- package/dist/upgrade/packages.js +49 -8
- package/package.json +20 -19
package/dist/stacks.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
1
5
|
import { log } from "@stacksjs/logging";
|
|
2
|
-
import {
|
|
6
|
+
import { registry } from "@stacksjs/registry";
|
|
3
7
|
import { copyFile, createFolder, deleteFile, deleteEmptyFolders, doesFolderExist, fs } from "@stacksjs/storage";
|
|
4
|
-
const STACK_DIRS = ["app", "config", "database", "resources", "routes", "public", "locales"];
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
const STACK_DIRS = ["app", "config", "database", "resources", "routes", "public", "locales", "docs"];
|
|
9
|
+
function stackLockPath(projectRoot) {
|
|
10
|
+
return join(projectRoot, "storage", "framework", "stacks.lock.json");
|
|
11
|
+
}
|
|
12
|
+
function stackBackupPath(projectRoot, stackName) {
|
|
13
|
+
return join(projectRoot, "storage", "framework", "stacks", "backups", stackName);
|
|
14
|
+
}
|
|
15
|
+
async function readStackLock(projectRoot) {
|
|
16
|
+
const lockPath = stackLockPath(projectRoot);
|
|
7
17
|
try {
|
|
8
18
|
if (fs.existsSync(lockPath)) {
|
|
9
19
|
const content = await Bun.file(lockPath).text();
|
|
@@ -16,35 +26,47 @@ async function readStackLock() {
|
|
|
16
26
|
stacks: {}
|
|
17
27
|
};
|
|
18
28
|
}
|
|
19
|
-
async function writeStackLock(lock) {
|
|
20
|
-
const lockPath =
|
|
29
|
+
async function writeStackLock(projectRoot, lock) {
|
|
30
|
+
const lockPath = stackLockPath(projectRoot), lockDir = dirname(lockPath);
|
|
21
31
|
if (!fs.existsSync(lockDir))
|
|
22
32
|
await createFolder(lockDir);
|
|
23
33
|
lock.generatedAt = new Date().toISOString();
|
|
24
34
|
await Bun.write(lockPath, JSON.stringify(lock, null, 2));
|
|
25
35
|
}
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
function resolveProjectRoot(project) {
|
|
37
|
+
return resolve(project || process.cwd());
|
|
38
|
+
}
|
|
39
|
+
function resolveRegistryEntry(name) {
|
|
40
|
+
const entry = registry.find((candidate) => {
|
|
41
|
+
if (typeof candidate === "string")
|
|
42
|
+
return candidate === name;
|
|
43
|
+
return candidate.name === name || candidate.package === name;
|
|
44
|
+
});
|
|
45
|
+
if (!entry || typeof entry === "string")
|
|
46
|
+
return null;
|
|
47
|
+
if (!entry.github)
|
|
48
|
+
return null;
|
|
49
|
+
return { name: entry.name, github: entry.github, package: entry.package };
|
|
50
|
+
}
|
|
51
|
+
async function checkoutRegistryStack(github) {
|
|
52
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "stacks-stack-")), checkoutPath = join(tempRoot, "source"), source = `https://github.com/${github}.git`;
|
|
53
|
+
if (await Bun.spawn(["git", "clone", "--depth", "1", "--quiet", source, checkoutPath], {
|
|
54
|
+
stdout: "inherit",
|
|
55
|
+
stderr: "inherit"
|
|
56
|
+
}).exited !== 0) {
|
|
57
|
+
rmSync(tempRoot, { recursive: !0, force: !0 });
|
|
58
|
+
throw Error(`Could not pull ${source}`);
|
|
46
59
|
}
|
|
47
|
-
return
|
|
60
|
+
return {
|
|
61
|
+
path: checkoutPath,
|
|
62
|
+
cleanup: () => rmSync(tempRoot, { recursive: !0, force: !0 })
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function isSafeStackDirectory(directory) {
|
|
66
|
+
return directory.length > 0 && !isAbsolute(directory) && !directory.includes("..") && !directory.includes("\\");
|
|
67
|
+
}
|
|
68
|
+
function fileChecksum(path) {
|
|
69
|
+
return new Bun.CryptoHasher("sha256").update(fs.readFileSync(path)).digest("hex");
|
|
48
70
|
}
|
|
49
71
|
function collectFiles(dir, baseDir) {
|
|
50
72
|
const files = [];
|
|
@@ -55,118 +77,151 @@ function collectFiles(dir, baseDir) {
|
|
|
55
77
|
const fullPath = join(dir, entry.name);
|
|
56
78
|
if (entry.isDirectory())
|
|
57
79
|
files.push(...collectFiles(fullPath, baseDir));
|
|
58
|
-
else
|
|
59
|
-
|
|
80
|
+
else if (entry.isSymbolicLink())
|
|
81
|
+
throw Error(`Stack contains an unsupported symbolic link: ${relative(baseDir, fullPath)}`);
|
|
82
|
+
else {
|
|
83
|
+
const relPath = relative(baseDir, fullPath);
|
|
84
|
+
if (!isSafeStackDirectory(relPath))
|
|
85
|
+
throw Error(`Stack contains an unsafe path: ${relPath}`);
|
|
86
|
+
files.push(relPath);
|
|
87
|
+
}
|
|
60
88
|
}
|
|
61
89
|
return files;
|
|
62
90
|
}
|
|
63
91
|
export async function installStack(options) {
|
|
64
|
-
const { name, force = !1, dryRun = !1, verbose = !1 } = options, conflict = force ? "overwrite" : options.conflict ?? "skip";
|
|
92
|
+
const { name, force = !1, dryRun = !1, verbose = !1 } = options, conflict = force ? "overwrite" : options.conflict ?? "skip", projectRoot = resolveProjectRoot(options.project);
|
|
65
93
|
log.info(`Installing stack "${name}"...`);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
94
|
+
if (!fs.existsSync(join(projectRoot, "package.json"))) {
|
|
95
|
+
log.error(`No Stacks project found at "${projectRoot}"`);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const registryEntry = resolveRegistryEntry(name);
|
|
99
|
+
if (!registryEntry) {
|
|
100
|
+
log.error(`Stack "${name}" is not in the Stacks registry. Run "buddy stack:list" to see available stacks.`);
|
|
69
101
|
return null;
|
|
70
102
|
}
|
|
71
|
-
let
|
|
103
|
+
let checkout;
|
|
72
104
|
try {
|
|
73
|
-
|
|
74
|
-
pkg = JSON.parse(pkgContent);
|
|
75
|
-
meta = pkg.stacks;
|
|
76
|
-
if (!meta?.name) {
|
|
77
|
-
log.error(`Package at "${stackPath}" does not have a valid "stacks" field in package.json`);
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
105
|
+
checkout = await checkoutRegistryStack(registryEntry.github);
|
|
80
106
|
} catch (error) {
|
|
81
|
-
log.error(
|
|
107
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
82
108
|
return null;
|
|
83
109
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
110
|
+
try {
|
|
111
|
+
const stackPath = checkout.path;
|
|
112
|
+
let pkg, meta;
|
|
113
|
+
try {
|
|
114
|
+
const pkgContent = await Bun.file(join(stackPath, "package.json")).text();
|
|
115
|
+
pkg = JSON.parse(pkgContent);
|
|
116
|
+
meta = pkg.stacks;
|
|
117
|
+
if (!meta?.name || meta.name !== registryEntry.name) {
|
|
118
|
+
log.error(`Registry source "${registryEntry.github}" does not declare stacks.name as "${registryEntry.name}"`);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
log.error(`Failed to read stack metadata: ${error}`);
|
|
123
|
+
return null;
|
|
95
124
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
copiedFiles.push(relPath);
|
|
113
|
-
} else {
|
|
114
|
-
log.info(`[dry-run] Would copy: ${relPath}`);
|
|
115
|
-
copiedFiles.push(relPath);
|
|
125
|
+
const lock = await readStackLock(projectRoot), packageName = pkg.name || registryEntry.package || name;
|
|
126
|
+
if (lock.stacks[packageName] && !force) {
|
|
127
|
+
log.warn(`Stack "${meta.name}" is already installed. Use --force to reinstall.`);
|
|
128
|
+
return lock.stacks[packageName];
|
|
129
|
+
}
|
|
130
|
+
const dirsToScan = meta.directories ?? STACK_DIRS;
|
|
131
|
+
if (dirsToScan.some((directory) => !isSafeStackDirectory(directory))) {
|
|
132
|
+
log.error(`Stack "${meta.name}" declares an unsafe project directory`);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const allFiles = [];
|
|
136
|
+
try {
|
|
137
|
+
for (const dir of dirsToScan) {
|
|
138
|
+
const dirPath = join(stackPath, dir);
|
|
139
|
+
if (fs.existsSync(dirPath))
|
|
140
|
+
allFiles.push(...collectFiles(dirPath, stackPath));
|
|
116
141
|
}
|
|
117
|
-
|
|
142
|
+
} catch (error) {
|
|
143
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
if (allFiles.length === 0) {
|
|
147
|
+
log.warn(`Stack "${meta.name}" has no project files to install`);
|
|
148
|
+
return null;
|
|
118
149
|
}
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
150
|
+
if (verbose)
|
|
151
|
+
log.info(`Pulled ${registryEntry.github} and found ${allFiles.length} project file(s)`);
|
|
152
|
+
const copiedFiles = [], skippedFiles = [], checksums = {};
|
|
153
|
+
for (const relPath of allFiles) {
|
|
154
|
+
const src = join(stackPath, relPath), dest = join(projectRoot, relPath);
|
|
155
|
+
if (dryRun) {
|
|
156
|
+
if (fs.existsSync(dest)) {
|
|
157
|
+
log.info(`[dry-run] Would ${conflict}: ${relPath}`);
|
|
158
|
+
if (conflict === "skip")
|
|
159
|
+
skippedFiles.push(relPath);
|
|
160
|
+
else
|
|
161
|
+
copiedFiles.push(relPath);
|
|
162
|
+
} else {
|
|
163
|
+
log.info(`[dry-run] Would copy: ${relPath}`);
|
|
164
|
+
copiedFiles.push(relPath);
|
|
134
165
|
}
|
|
135
|
-
|
|
136
|
-
if (verbose)
|
|
137
|
-
log.warn(`Overwriting: ${relPath}`);
|
|
138
|
-
break;
|
|
166
|
+
continue;
|
|
139
167
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
168
|
+
if (fs.existsSync(dest))
|
|
169
|
+
switch (conflict) {
|
|
170
|
+
case "skip":
|
|
171
|
+
if (verbose)
|
|
172
|
+
log.warn(`Skipping (file exists): ${relPath}`);
|
|
173
|
+
skippedFiles.push(relPath);
|
|
174
|
+
continue;
|
|
175
|
+
case "backup": {
|
|
176
|
+
const backupDir = stackBackupPath(projectRoot, meta.name), backupDest = join(backupDir, relPath), backupDestDir = dirname(backupDest);
|
|
177
|
+
if (!fs.existsSync(backupDestDir))
|
|
178
|
+
await createFolder(backupDestDir);
|
|
179
|
+
copyFile(dest, backupDest);
|
|
180
|
+
if (verbose)
|
|
181
|
+
log.info(`Backed up: ${relPath}`);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "overwrite":
|
|
185
|
+
if (verbose)
|
|
186
|
+
log.warn(`Overwriting: ${relPath}`);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
const destDir = dirname(dest);
|
|
190
|
+
if (!fs.existsSync(destDir))
|
|
191
|
+
await createFolder(destDir);
|
|
192
|
+
copyFile(src, dest);
|
|
193
|
+
copiedFiles.push(relPath);
|
|
194
|
+
checksums[relPath] = fileChecksum(dest);
|
|
195
|
+
if (verbose)
|
|
196
|
+
log.info(`Copied: ${relPath}`);
|
|
197
|
+
}
|
|
198
|
+
const entry = {
|
|
199
|
+
packageName,
|
|
200
|
+
name: meta.name,
|
|
201
|
+
version: pkg.version || "0.0.0",
|
|
202
|
+
installedAt: new Date().toISOString(),
|
|
203
|
+
files: copiedFiles,
|
|
204
|
+
checksums,
|
|
205
|
+
source: registryEntry.github,
|
|
206
|
+
skipped: skippedFiles,
|
|
207
|
+
conflictStrategy: conflict
|
|
208
|
+
};
|
|
209
|
+
if (dryRun) {
|
|
210
|
+
log.info(`[dry-run] Would install ${copiedFiles.length} file(s), skip ${skippedFiles.length} file(s)`);
|
|
211
|
+
return entry;
|
|
212
|
+
}
|
|
213
|
+
lock.stacks[packageName] = entry;
|
|
214
|
+
await writeStackLock(projectRoot, lock);
|
|
215
|
+
log.success(`Installed stack "${meta.name}" from ${registryEntry.github} (${copiedFiles.length} file(s) copied, ${skippedFiles.length} skipped)`);
|
|
216
|
+
return entry;
|
|
217
|
+
} finally {
|
|
218
|
+
checkout.cleanup();
|
|
151
219
|
}
|
|
152
|
-
const entry = {
|
|
153
|
-
packageName,
|
|
154
|
-
name: meta.name,
|
|
155
|
-
version: pkg.version || "0.0.0",
|
|
156
|
-
installedAt: new Date().toISOString(),
|
|
157
|
-
files: copiedFiles,
|
|
158
|
-
skipped: skippedFiles,
|
|
159
|
-
conflictStrategy: conflict
|
|
160
|
-
};
|
|
161
|
-
lock.stacks[packageName] = entry;
|
|
162
|
-
await writeStackLock(lock);
|
|
163
|
-
log.success(`Installed stack "${meta.name}" (${copiedFiles.length} file(s) copied, ${skippedFiles.length} skipped)`);
|
|
164
|
-
return entry;
|
|
165
220
|
}
|
|
166
221
|
export async function uninstallStack(options) {
|
|
167
|
-
const { name, force = !1, verbose = !1 } = options;
|
|
222
|
+
const { name, force = !1, verbose = !1 } = options, projectRoot = resolveProjectRoot(options.project);
|
|
168
223
|
log.info(`Uninstalling stack "${name}"...`);
|
|
169
|
-
const lock = await readStackLock();
|
|
224
|
+
const lock = await readStackLock(projectRoot);
|
|
170
225
|
let entryKey = null, entry = null;
|
|
171
226
|
for (const [key, value] of Object.entries(lock.stacks))
|
|
172
227
|
if (value.name === name || key === name) {
|
|
@@ -179,24 +234,20 @@ export async function uninstallStack(options) {
|
|
|
179
234
|
return !1;
|
|
180
235
|
}
|
|
181
236
|
let removedCount = 0;
|
|
237
|
+
const remainingFiles = [];
|
|
182
238
|
for (const relPath of entry.files) {
|
|
183
|
-
const filePath =
|
|
239
|
+
const filePath = join(projectRoot, relPath);
|
|
184
240
|
if (!fs.existsSync(filePath)) {
|
|
185
241
|
if (verbose)
|
|
186
242
|
log.info(`Already removed: ${relPath}`);
|
|
187
243
|
continue;
|
|
188
244
|
}
|
|
189
245
|
if (!force) {
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (srcContent !== destContent) {
|
|
196
|
-
log.warn(`Skipping modified file: ${relPath} (use --force to remove)`);
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
246
|
+
const installedChecksum = entry.checksums?.[relPath];
|
|
247
|
+
if (!installedChecksum || fileChecksum(filePath) !== installedChecksum) {
|
|
248
|
+
log.warn(`Skipping modified file: ${relPath} (use --force to remove)`);
|
|
249
|
+
remainingFiles.push(relPath);
|
|
250
|
+
continue;
|
|
200
251
|
}
|
|
201
252
|
}
|
|
202
253
|
await deleteFile(filePath);
|
|
@@ -204,11 +255,19 @@ export async function uninstallStack(options) {
|
|
|
204
255
|
if (verbose)
|
|
205
256
|
log.info(`Removed: ${relPath}`);
|
|
206
257
|
}
|
|
207
|
-
|
|
258
|
+
if (remainingFiles.length > 0) {
|
|
259
|
+
entry.files = remainingFiles;
|
|
260
|
+
entry.checksums = Object.fromEntries(remainingFiles.map((file) => [file, entry.checksums?.[file]]).filter((item) => typeof item[1] === "string"));
|
|
261
|
+
lock.stacks[entryKey] = entry;
|
|
262
|
+
await writeStackLock(projectRoot, lock);
|
|
263
|
+
log.warn(`Stack "${entry.name}" is still tracked because ${remainingFiles.length} modified file(s) remain`);
|
|
264
|
+
return !1;
|
|
265
|
+
}
|
|
266
|
+
const backupDir = stackBackupPath(projectRoot, entry.name);
|
|
208
267
|
if (fs.existsSync(backupDir) && doesFolderExist(backupDir)) {
|
|
209
268
|
const backupFiles = collectFiles(backupDir, backupDir);
|
|
210
269
|
for (const relPath of backupFiles) {
|
|
211
|
-
const backupSrc = join(backupDir, relPath), dest =
|
|
270
|
+
const backupSrc = join(backupDir, relPath), dest = join(projectRoot, relPath), destDir = dirname(dest);
|
|
212
271
|
if (!fs.existsSync(destDir))
|
|
213
272
|
await createFolder(destDir);
|
|
214
273
|
copyFile(backupSrc, dest);
|
|
@@ -218,38 +277,30 @@ export async function uninstallStack(options) {
|
|
|
218
277
|
fs.rmSync(backupDir, { recursive: !0, force: !0 });
|
|
219
278
|
}
|
|
220
279
|
for (const dir of STACK_DIRS) {
|
|
221
|
-
const dirPath =
|
|
280
|
+
const dirPath = join(projectRoot, dir);
|
|
222
281
|
if (fs.existsSync(dirPath))
|
|
223
282
|
await deleteEmptyFolders(dirPath);
|
|
224
283
|
}
|
|
225
284
|
delete lock.stacks[entryKey];
|
|
226
|
-
await writeStackLock(lock);
|
|
285
|
+
await writeStackLock(projectRoot, lock);
|
|
227
286
|
log.success(`Uninstalled stack "${entry.name}" (${removedCount} file(s) removed)`);
|
|
228
287
|
return !0;
|
|
229
288
|
}
|
|
230
|
-
export async function listStacks() {
|
|
231
|
-
const entries = [],
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
version: pkg.version || "0.0.0",
|
|
246
|
-
description: pkg.stacks.description,
|
|
247
|
-
installed,
|
|
248
|
-
installedAt: lockEntry?.installedAt,
|
|
249
|
-
fileCount: lockEntry?.files.length
|
|
250
|
-
});
|
|
251
|
-
} catch {}
|
|
252
|
-
}
|
|
289
|
+
export async function listStacks(project) {
|
|
290
|
+
const entries = [], projectRoot = resolveProjectRoot(project), lock = await readStackLock(projectRoot);
|
|
291
|
+
for (const candidate of registry) {
|
|
292
|
+
if (typeof candidate === "string")
|
|
293
|
+
continue;
|
|
294
|
+
const packageName = candidate.package || candidate.name, lockEntry = lock.stacks[packageName];
|
|
295
|
+
entries.push({
|
|
296
|
+
name: candidate.name,
|
|
297
|
+
packageName,
|
|
298
|
+
version: lockEntry?.version || "latest",
|
|
299
|
+
description: candidate.description,
|
|
300
|
+
installed: Boolean(lockEntry),
|
|
301
|
+
installedAt: lockEntry?.installedAt,
|
|
302
|
+
fileCount: lockEntry?.files.length
|
|
303
|
+
});
|
|
253
304
|
}
|
|
254
305
|
for (const [packageName, entry] of Object.entries(lock.stacks))
|
|
255
306
|
if (!entries.some((e) => e.packageName === packageName))
|
package/dist/templates.js
CHANGED
|
@@ -62,22 +62,25 @@ export default new Action({
|
|
|
62
62
|
return { ok: true, userId: user.id, data }
|
|
63
63
|
},
|
|
64
64
|
})`,
|
|
65
|
-
component: `<script
|
|
66
|
-
|
|
65
|
+
component: `<script server>
|
|
66
|
+
interface ComponentProps {
|
|
67
|
+
message?: string
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const { message = 'Hello World component created' } = defineProps<ComponentProps>()
|
|
67
71
|
</script>
|
|
68
72
|
|
|
69
73
|
<template>
|
|
70
|
-
<div>
|
|
71
|
-
Some HTML block
|
|
72
|
-
</div>
|
|
74
|
+
<div>{{ message }}</div>
|
|
73
75
|
</template>`,
|
|
74
|
-
page: `<script
|
|
75
|
-
|
|
76
|
+
page: `<script server>
|
|
77
|
+
const heading = 'Hello World page created'
|
|
76
78
|
</script>
|
|
77
79
|
|
|
78
80
|
<template>
|
|
79
81
|
<div>
|
|
80
|
-
|
|
82
|
+
<h1>{{ heading }}</h1>
|
|
83
|
+
<p>Visit /{0}</p>
|
|
81
84
|
</div>
|
|
82
85
|
</template>`,
|
|
83
86
|
function: `// reactive state
|
package/dist/test/feature.js
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { projectPath } from "@stacksjs/path";
|
|
4
|
-
const proc = Bun.spawn(["sh", "-c", "bun test ./tests/feature/**"], {
|
|
5
|
-
cwd: projectPath(),
|
|
6
|
-
stdio: ["inherit", "inherit", "inherit"]
|
|
7
|
-
}), exitCode = await proc.exited;
|
|
8
|
-
if (exitCode !== 0) {
|
|
9
|
-
log.error("Tests failed");
|
|
10
|
-
process.exit(exitCode ?? 1);
|
|
11
|
-
}
|
|
1
|
+
import { runTestSuites } from "./runner";
|
|
2
|
+
await runTestSuites(["feature"]);
|
package/dist/test/index.js
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { projectPath } from "@stacksjs/path";
|
|
4
|
-
const proc = Bun.spawn(["sh", "-c", "bun test --timeout 30000 ./tests/feature/** ./tests/integration/** ./tests/unit/**"], {
|
|
5
|
-
cwd: projectPath(),
|
|
6
|
-
stdio: ["inherit", "inherit", "inherit"]
|
|
7
|
-
}), exitCode = await proc.exited;
|
|
8
|
-
if (exitCode !== 0) {
|
|
9
|
-
log.error("Tests failed");
|
|
10
|
-
process.exit(exitCode ?? 1);
|
|
11
|
-
}
|
|
1
|
+
import { runTestSuites } from "./runner";
|
|
2
|
+
await runTestSuites([""], 30000);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runTestSuites(suites: string[], timeout?: number): Promise<void>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { log } from "@stacksjs/cli";
|
|
5
|
+
import { projectPath } from "@stacksjs/path";
|
|
6
|
+
const testFiles = new Bun.Glob("**/*.{test,spec}.{js,jsx,ts,tsx,mjs,mts,cjs,cts}");
|
|
7
|
+
export async function runTestSuites(suites, timeout) {
|
|
8
|
+
const project = projectPath(), filters = suites.flatMap((suite) => {
|
|
9
|
+
const suiteDirectory = join(project, "tests", suite);
|
|
10
|
+
if (!existsSync(suiteDirectory))
|
|
11
|
+
return [];
|
|
12
|
+
return [...testFiles.scanSync({ cwd: suiteDirectory, onlyFiles: !0 })].map((file) => suite ? `./tests/${suite}/${file}` : `./tests/${file}`);
|
|
13
|
+
});
|
|
14
|
+
if (filters.length === 0) {
|
|
15
|
+
log.info("No matching tests found.");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const args = ["bun", "test"];
|
|
19
|
+
if (timeout)
|
|
20
|
+
args.push("--timeout", String(timeout));
|
|
21
|
+
args.push(...filters);
|
|
22
|
+
const exitCode = await Bun.spawn(args, {
|
|
23
|
+
cwd: project,
|
|
24
|
+
stdio: ["inherit", "inherit", "inherit"]
|
|
25
|
+
}).exited;
|
|
26
|
+
if (exitCode !== 0) {
|
|
27
|
+
log.error("Tests failed");
|
|
28
|
+
process.exit(exitCode ?? 1);
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/test/unit.js
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { projectPath } from "@stacksjs/path";
|
|
4
|
-
const proc = Bun.spawn(["sh", "-c", "bun test ./tests/unit/**"], {
|
|
5
|
-
cwd: projectPath(),
|
|
6
|
-
stdio: ["inherit", "inherit", "inherit"]
|
|
7
|
-
}), exitCode = await proc.exited;
|
|
8
|
-
if (exitCode !== 0) {
|
|
9
|
-
log.error("Tests failed");
|
|
10
|
-
process.exit(exitCode ?? 1);
|
|
11
|
-
}
|
|
1
|
+
import { runTestSuites } from "./runner";
|
|
2
|
+
await runTestSuites(["unit"]);
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
export declare function hasStacksDependency(pkg: PkgJson): boolean;
|
|
2
|
+
export declare function standalonePackageUpdateCommand(): string;
|
|
3
|
+
/** Strip a range operator (`^`, `~`, `>=`, …) so a declared range compares to a bare version. */
|
|
4
|
+
export declare function baseVersion(range: string): string;
|
|
5
|
+
/*` tooling
|
|
6
|
+
* (e.g. `better-dx`) are excluded, so they are never force-bumped to a framework
|
|
7
|
+
* version they don't publish (stacksjs/stacks#2078).
|
|
8
|
+
*/
|
|
9
|
+
export declare function lockstepPackages(metaDeps: Record<string, string>, target: string): Set<string>;
|
|
10
|
+
/*` packages — is left
|
|
11
|
+
* untouched. Returns the set of applied changes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function applyBumps(pkg: PkgJson, target: string, lockstep: Set<string>): Array<{ name: string, from: string, to: string }>;
|
|
1
14
|
/**
|
|
2
15
|
* Upgrade a node_modules app to a published framework version. Called by the
|
|
3
16
|
* framework upgrade script when no vendored `storage/framework/core` exists.
|
|
@@ -12,3 +25,8 @@ export declare interface PackageUpgradeOptions {
|
|
|
12
25
|
dryRun?: boolean
|
|
13
26
|
noPostinstall?: boolean
|
|
14
27
|
}
|
|
28
|
+
declare interface PkgJson {
|
|
29
|
+
dependencies?: Record<string, string>
|
|
30
|
+
devDependencies?: Record<string, string>
|
|
31
|
+
[key: string]: unknown
|
|
32
|
+
}
|