@owdproject/core 3.1.4-beta.0 → 3.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.
Files changed (64) hide show
  1. package/README.md +6 -6
  2. package/bin/cli.js +395 -0
  3. package/bin/desktop.js +5 -0
  4. package/bin/lib/catalog.js +293 -0
  5. package/bin/lib/config.js +109 -0
  6. package/bin/lib/desktopConfig.js +56 -0
  7. package/bin/lib/generateTemplate.js +389 -0
  8. package/bin/lib/install.js +408 -0
  9. package/bin/lib/migrateModulePlayground.config.mjs +164 -0
  10. package/bin/lib/migrateModulePlayground.mjs +313 -0
  11. package/bin/lib/npmVersions.js +124 -0
  12. package/bin/lib/playgroundContext.js +188 -0
  13. package/bin/lib/scaffold.js +197 -0
  14. package/bin/lib/status.js +289 -0
  15. package/bin/lib/validateModule.js +683 -0
  16. package/bin/lib/workspace.js +182 -0
  17. package/bin/owd.js +2 -25
  18. package/bin/tui.js +1379 -0
  19. package/dist/module.d.mts +13 -0
  20. package/dist/module.json +9 -0
  21. package/dist/module.mjs +219 -0
  22. package/dist/types.d.mts +7 -0
  23. package/module.ts +53 -19
  24. package/package.json +78 -22
  25. package/runtime/components/Core/Application/CoreApplicationRender.vue +3 -1
  26. package/runtime/components/Core/Application/CoreApplicationWindowsRender.vue +28 -7
  27. package/runtime/components/Core/Desktop/CoreDesktop.vue +11 -7
  28. package/runtime/components/Core/Window/CoreWindow.vue +45 -9
  29. package/runtime/components/Core/Window/CoreWindowNav.vue +26 -8
  30. package/runtime/composables/useApplicationEntries.ts +1 -1
  31. package/runtime/composables/useApplicationManager.ts +282 -4
  32. package/runtime/composables/useDesktopManager.ts +46 -4
  33. package/runtime/composables/useOwdDialogs.ts +13 -0
  34. package/runtime/composables/useTerminalManager.ts +62 -3
  35. package/runtime/composables/useWorkspaceOverview.ts +41 -0
  36. package/runtime/core/controllers/ApplicationController.ts +120 -30
  37. package/runtime/core/controllers/WindowController.ts +5 -10
  38. package/runtime/dialogs/owdDialogProvider.ts +81 -0
  39. package/runtime/plugins/01.owd-dialogs-fallback.ts +15 -0
  40. package/runtime/stores/storeApplicationMeta.ts +56 -12
  41. package/runtime/stores/storeDesktop.ts +79 -0
  42. package/runtime/stores/storeDesktopDefaultApps.ts +53 -0
  43. package/runtime/stores/storeDesktopExplorer.ts +74 -0
  44. package/runtime/stores/storeDesktopWorkspace.ts +14 -2
  45. package/runtime/utils/captureElementToCanvas.ts +21 -0
  46. package/runtime/utils/explorerEntryPath.ts +10 -0
  47. package/runtime/utils/resolveDesktopConfigPath.ts +52 -0
  48. package/runtime/utils/validateOwdUserConfig.ts +44 -0
  49. package/types/index.d.ts +64 -12
  50. package/CHANGELOG.md +0 -25
  51. package/project.json +0 -4
  52. package/runtime/composables/useClipboardFs.ts +0 -25
  53. package/runtime/core/managers/ApplicationManager.ts +0 -281
  54. package/runtime/core/managers/DesktopManager.ts +0 -47
  55. package/runtime/core/managers/TerminalManager.ts +0 -48
  56. package/runtime/stores/storeApplication.ts +0 -235
  57. package/test/basic.test.ts +0 -15
  58. package/test/fixtures/basic/app.vue +0 -5
  59. package/test/fixtures/basic/nuxt.config.ts +0 -5
  60. package/test/fixtures/basic/package.json +0 -5
  61. package/tsconfig.app.json +0 -31
  62. package/tsconfig.json +0 -12
  63. package/tsconfig.spec.json +0 -37
  64. package/vitest.config.ts +0 -24
@@ -0,0 +1,389 @@
1
+ import { execSync } from 'node:child_process'
2
+ import {
3
+ cpSync,
4
+ existsSync,
5
+ mkdtempSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ readdirSync,
9
+ rmSync,
10
+ statSync,
11
+ writeFileSync,
12
+ } from 'node:fs'
13
+ import { tmpdir } from 'node:os'
14
+ import { basename, dirname, join, relative, resolve } from 'node:path'
15
+ import { fileURLToPath } from 'node:url'
16
+ import {
17
+ fetchLatestVersions,
18
+ isOwdScopedPackage,
19
+ resolveLatestVersions,
20
+ } from './npmVersions.js'
21
+
22
+ const __dirname = dirname(fileURLToPath(import.meta.url))
23
+ const BLUEPRINT_ROOT = resolve(__dirname, '../../template-blueprint')
24
+
25
+ export const STARTER_DESKTOP_DEPS = [
26
+ '@owdproject/core',
27
+ '@owdproject/theme-nova',
28
+ '@owdproject/app-about',
29
+ 'h3',
30
+ ]
31
+
32
+ /** Used when a starter package is not on npm yet (before first publish). */
33
+ export const TEMPLATE_NPM_FALLBACK_VERSIONS = {
34
+ '@owdproject/theme-nova': '0.0.1',
35
+ }
36
+
37
+ const DESKTOP_COPY_FILES = [
38
+ 'tsconfig.json',
39
+ 'tsconfig.app.json',
40
+ 'tsconfig.spec.json',
41
+ '.npmrc',
42
+ ]
43
+
44
+ const ROOT_COPY_FROM_MONOREPO = [
45
+ 'nx.json',
46
+ 'tsconfig.base.json',
47
+ 'tsconfig.json',
48
+ '.editorconfig',
49
+ '.npmrc',
50
+ '.prettierrc',
51
+ '.prettierignore',
52
+ ]
53
+
54
+ const DESKTOP_SKIP_NAMES = new Set([
55
+ 'node_modules',
56
+ '.nuxt',
57
+ '.output',
58
+ 'dist',
59
+ '.env',
60
+ '.data',
61
+ 'vitest.config.ts',
62
+ 'tsconfig.tsbuildinfo',
63
+ 'package.json',
64
+ 'desktop.config.ts',
65
+ 'nuxt.config.ts',
66
+ 'app',
67
+ ])
68
+
69
+ const BLUEPRINT_SKIP_NAMES = new Set(['root-package.json'])
70
+
71
+ /**
72
+ * @param {string} blueprintRoot
73
+ */
74
+ export function resolveBlueprintRoot(blueprintRoot = BLUEPRINT_ROOT) {
75
+ if (!existsSync(join(blueprintRoot, 'pnpm-workspace.yaml'))) {
76
+ throw new Error(`Template blueprint not found at ${blueprintRoot}`)
77
+ }
78
+ return blueprintRoot
79
+ }
80
+
81
+ /**
82
+ * @param {string} src
83
+ * @param {string} dest
84
+ * @param {Set<string>} [skipNames]
85
+ */
86
+ function copyTree(src, dest, skipNames = new Set()) {
87
+ if (!existsSync(src)) return
88
+ const stat = statSync(src)
89
+ if (stat.isFile()) {
90
+ mkdirSync(dirname(dest), { recursive: true })
91
+ cpSync(src, dest)
92
+ return
93
+ }
94
+
95
+ mkdirSync(dest, { recursive: true })
96
+ for (const name of readdirSync(src)) {
97
+ if (skipNames.has(name)) continue
98
+ copyTree(join(src, name), join(dest, name), skipNames)
99
+ }
100
+ }
101
+
102
+ /**
103
+ * @param {string} workspaceRoot
104
+ */
105
+ function readMonorepoPackageJson(workspaceRoot) {
106
+ const path = join(workspaceRoot, 'package.json')
107
+ return JSON.parse(readFileSync(path, 'utf8'))
108
+ }
109
+
110
+ /**
111
+ * @param {string} workspaceRoot
112
+ * @param {{ useCache?: boolean }} [options]
113
+ */
114
+ export async function resolveTemplateVersions(workspaceRoot, options = {}) {
115
+ const monorepo = readMonorepoPackageJson(workspaceRoot)
116
+ const monorepoDevDeps = monorepo.devDependencies ?? {}
117
+
118
+ const owdPackages = [
119
+ ...new Set([
120
+ ...STARTER_DESKTOP_DEPS,
121
+ ...Object.keys(monorepoDevDeps).filter(isOwdScopedPackage),
122
+ ]),
123
+ ]
124
+
125
+ const npmLatest = await resolveLatestVersions(workspaceRoot, owdPackages, options)
126
+
127
+ /** @type {Record<string, string>} */
128
+ const versions = { ...monorepoDevDeps, ...npmLatest }
129
+ for (const name of STARTER_DESKTOP_DEPS) {
130
+ versions[name] =
131
+ npmLatest[name] ?? TEMPLATE_NPM_FALLBACK_VERSIONS[name] ?? monorepoDevDeps[name]
132
+ if (!versions[name]) {
133
+ throw new Error(`Could not resolve version for ${name}`)
134
+ }
135
+ }
136
+
137
+ return versions
138
+ }
139
+
140
+ /**
141
+ * @param {Record<string, string>} versions
142
+ */
143
+ function buildTemplateRootPackageJson(versions) {
144
+ const base = JSON.parse(readFileSync(join(BLUEPRINT_ROOT, 'root-package.json'), 'utf8'))
145
+ /** @type {Record<string, string>} */
146
+ const devDependencies = {}
147
+ for (const [name, range] of Object.entries(versions)) {
148
+ if (name === 'h3') continue
149
+ devDependencies[name] = range
150
+ }
151
+ base.devDependencies = Object.fromEntries(
152
+ Object.entries(devDependencies).sort(([a], [b]) => a.localeCompare(b)),
153
+ )
154
+ return `${JSON.stringify(base, null, 2)}\n`
155
+ }
156
+
157
+ /**
158
+ * @param {Record<string, string>} versions
159
+ */
160
+ function buildTemplateDesktopPackageJson(versions) {
161
+ /** @type {Record<string, string>} */
162
+ const dependencies = {}
163
+ for (const name of STARTER_DESKTOP_DEPS) {
164
+ dependencies[name] = versions[name]
165
+ }
166
+
167
+ const pkg = {
168
+ name: '@owdproject/client',
169
+ private: true,
170
+ nx: { name: 'desktop' },
171
+ scripts: {
172
+ build: 'nuxt generate',
173
+ dev: 'nuxt dev --host',
174
+ generate: 'nuxt generate --dev',
175
+ postinstall: 'nuxt prepare',
176
+ preview: 'nuxt preview',
177
+ },
178
+ dependencies: Object.fromEntries(
179
+ Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)),
180
+ ),
181
+ }
182
+
183
+ return `${JSON.stringify(pkg, null, 2)}\n`
184
+ }
185
+
186
+ /**
187
+ * @param {string} srcPath
188
+ */
189
+ function readProjectJson(srcPath) {
190
+ const raw = readFileSync(srcPath, 'utf8')
191
+ const data = JSON.parse(raw)
192
+ if (data.name === 'desktopx') data.name = 'desktop'
193
+ return `${JSON.stringify(data, null, 2)}\n`
194
+ }
195
+
196
+ /**
197
+ * @param {string} workspaceRoot
198
+ * @param {string} outputRoot
199
+ * @param {{ versions: Record<string, string> }} ctx
200
+ */
201
+ function writeGeneratedFiles(workspaceRoot, outputRoot, ctx) {
202
+ const { versions } = ctx
203
+ const desktopSrc = join(workspaceRoot, 'desktop')
204
+ const desktopOut = join(outputRoot, 'desktop')
205
+ const blueprint = resolveBlueprintRoot()
206
+
207
+ mkdirSync(outputRoot, { recursive: true })
208
+
209
+ copyTree(blueprint, outputRoot, BLUEPRINT_SKIP_NAMES)
210
+
211
+ writeFileSync(join(outputRoot, 'package.json'), buildTemplateRootPackageJson(versions))
212
+
213
+ mkdirSync(desktopOut, { recursive: true })
214
+ for (const file of DESKTOP_COPY_FILES) {
215
+ const src = join(desktopSrc, file)
216
+ if (existsSync(src)) {
217
+ cpSync(src, join(desktopOut, file))
218
+ }
219
+ }
220
+
221
+ const i18nBlueprint = join(blueprint, 'desktop/i18n')
222
+ if (existsSync(i18nBlueprint)) {
223
+ copyTree(i18nBlueprint, join(desktopOut, 'i18n'))
224
+ }
225
+
226
+ const projectJsonSrc = join(desktopSrc, 'project.json')
227
+ if (existsSync(projectJsonSrc)) {
228
+ writeFileSync(join(desktopOut, 'project.json'), readProjectJson(projectJsonSrc))
229
+ }
230
+
231
+ for (const file of ROOT_COPY_FROM_MONOREPO) {
232
+ const src = join(workspaceRoot, file)
233
+ if (existsSync(src)) {
234
+ cpSync(src, join(outputRoot, file))
235
+ }
236
+ }
237
+
238
+ writeFileSync(join(desktopOut, 'package.json'), buildTemplateDesktopPackageJson(versions))
239
+ }
240
+
241
+ /**
242
+ * @param {string} dir
243
+ * @returns {string[]}
244
+ */
245
+ function listFilesRecursive(dir) {
246
+ if (!existsSync(dir)) return []
247
+ /** @type {string[]} */
248
+ const files = []
249
+ for (const name of readdirSync(dir)) {
250
+ const path = join(dir, name)
251
+ const stat = statSync(path)
252
+ if (stat.isDirectory()) {
253
+ if (name === 'node_modules' || name === '.git') continue
254
+ files.push(...listFilesRecursive(path))
255
+ } else {
256
+ files.push(path)
257
+ }
258
+ }
259
+ return files
260
+ }
261
+
262
+ /**
263
+ * @param {string} aRoot
264
+ * @param {string} bRoot
265
+ */
266
+ export function diffTemplateTrees(aRoot, bRoot) {
267
+ const aFiles = new Set(listFilesRecursive(aRoot).map((p) => relative(aRoot, p)))
268
+ const bFiles = new Set(listFilesRecursive(bRoot).map((p) => relative(bRoot, p)))
269
+ /** @type {{ path: string, type: 'missing' | 'extra' | 'changed' }[]} */
270
+ const diffs = []
271
+
272
+ for (const rel of aFiles) {
273
+ if (!bFiles.has(rel)) {
274
+ diffs.push({ path: rel, type: 'missing' })
275
+ continue
276
+ }
277
+ const a = readFileSync(join(aRoot, rel))
278
+ const b = readFileSync(join(bRoot, rel))
279
+ if (!a.equals(b)) diffs.push({ path: rel, type: 'changed' })
280
+ }
281
+
282
+ for (const rel of bFiles) {
283
+ if (!aFiles.has(rel)) diffs.push({ path: rel, type: 'extra' })
284
+ }
285
+
286
+ return diffs.sort((x, y) => x.path.localeCompare(y.path))
287
+ }
288
+
289
+ /**
290
+ * @param {string} workspaceRoot
291
+ * @param {{ dryRun?: boolean, check?: boolean, useCache?: boolean }} [options]
292
+ */
293
+ export async function generateTemplate(workspaceRoot, options = {}) {
294
+ const { dryRun = false, check = false, useCache = true } = options
295
+ const templateOut = join(workspaceRoot, 'template')
296
+ const desktopPath = join(workspaceRoot, 'desktop')
297
+
298
+ if (!existsSync(desktopPath)) {
299
+ throw new Error(`Monorepo desktop/ not found at ${desktopPath}`)
300
+ }
301
+ resolveBlueprintRoot()
302
+
303
+ const versions = await resolveTemplateVersions(workspaceRoot, { useCache })
304
+
305
+ if (dryRun) {
306
+ console.log('\nTemplate generation plan (dry run)\n')
307
+ console.log('Output:', templateOut)
308
+ console.log('\nStarter desktop dependencies:')
309
+ for (const name of STARTER_DESKTOP_DEPS) {
310
+ console.log(` ${name} ${versions[name]}`)
311
+ }
312
+ console.log('\nRoot devDependencies (@owdproject/* from npm):')
313
+ for (const [name, range] of Object.entries(versions).sort(([a], [b]) => a.localeCompare(b))) {
314
+ if (isOwdScopedPackage(name)) console.log(` ${name} ${range}`)
315
+ }
316
+ console.log('\nSources:')
317
+ console.log(` blueprint ${BLUEPRINT_ROOT}`)
318
+ console.log(` desktop/ ${desktopPath}`)
319
+ console.log(` monorepo ${workspaceRoot}`)
320
+ console.log('')
321
+ return { versions, outputRoot: templateOut }
322
+ }
323
+
324
+ const outputRoot = check ? mkdtempSync(join(tmpdir(), 'owd-template-check-')) : templateOut
325
+
326
+ try {
327
+ if (!check && existsSync(templateOut)) {
328
+ rmSync(templateOut, { recursive: true, force: true })
329
+ }
330
+
331
+ writeGeneratedFiles(workspaceRoot, outputRoot, { versions })
332
+
333
+ if (check) {
334
+ const diffs = diffTemplateTrees(templateOut, outputRoot)
335
+ if (diffs.length === 0) {
336
+ console.log('\n✓ template/ is up to date\n')
337
+ return { ok: true, versions }
338
+ }
339
+
340
+ console.error('\n✗ template/ is out of date\n')
341
+ for (const d of diffs.slice(0, 40)) {
342
+ const label =
343
+ d.type === 'missing'
344
+ ? 'only in committed template'
345
+ : d.type === 'extra'
346
+ ? 'missing from committed template'
347
+ : 'changed'
348
+ console.error(` ${label}: ${d.path}`)
349
+ }
350
+ if (diffs.length > 40) {
351
+ console.error(` … and ${diffs.length - 40} more`)
352
+ }
353
+ console.error('\nRun: pnpm desktop template\n')
354
+ return { ok: false, versions, diffs }
355
+ }
356
+
357
+ console.log('\n✓ Generated template/\n')
358
+ console.log('Starter packages:')
359
+ for (const name of STARTER_DESKTOP_DEPS) {
360
+ console.log(` ${name} ${versions[name]}`)
361
+ }
362
+ console.log('\nCommit template/ when ready.\n')
363
+ return { ok: true, versions, outputRoot }
364
+ } finally {
365
+ if (check && existsSync(outputRoot)) {
366
+ rmSync(outputRoot, { recursive: true, force: true })
367
+ }
368
+ }
369
+ }
370
+
371
+ /**
372
+ * @param {{ workspaceRoot: string, dryRun?: boolean, check?: boolean }} options
373
+ */
374
+ export async function runGenerateTemplateCli(options) {
375
+ const { workspaceRoot, dryRun = false, check = false } = options
376
+
377
+ if (!workspaceRoot) {
378
+ console.error('\n✗ Not inside an OWD workspace.\n')
379
+ process.exit(1)
380
+ }
381
+
382
+ try {
383
+ const result = await generateTemplate(workspaceRoot, { dryRun, check })
384
+ if (check && result.ok === false) process.exit(1)
385
+ } catch (error) {
386
+ console.error(`\n✗ ${error.message ?? String(error)}\n`)
387
+ process.exit(1)
388
+ }
389
+ }