@publish.os/nextjs 0.0.1-alpha.8 → 0.0.1-alpha.90
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/compiler-assets/nextjs/defaults/app/globals.css +1 -0
- package/compiler-assets/nextjs/defaults/group/index.tsx +50 -0
- package/compiler-assets/nextjs/defaults/group/layout.tsx +7 -3
- package/compiler-assets/nextjs/defaults/group/page.tsx +35 -43
- package/compiler-assets/nextjs/defaults/project/next.config.mjs +22 -11
- package/compiler-assets/nextjs/defaults/project/tsconfig.json.template +1 -4
- package/compiler-assets/nextjs/defaults/root/css/animation.css +131 -0
- package/compiler-assets/nextjs/defaults/root/css/components/dialog.css +4 -1
- package/compiler-assets/nextjs/defaults/root/css/components/menu.css +1 -1
- package/compiler-assets/nextjs/defaults/root/css/components/navigation-menu.css +1 -1
- package/compiler-assets/nextjs/defaults/root/css/components/popover.css +1 -1
- package/compiler-assets/nextjs/defaults/root/css/components/select.css +1 -1
- package/compiler-assets/nextjs/defaults/root/css/components/tooltip.css +1 -1
- package/compiler-assets/nextjs/defaults/root/css/shadow.css +6 -25
- package/compiler-assets/nextjs/defaults/root/css/typography.css +51 -68
- package/compiler-assets/nextjs/defaults/root/globals.css +25 -0
- package/compiler-assets/nextjs/defaults/root/{page.tsx → index.tsx} +6 -2
- package/compiler-assets/nextjs/defaults/root/layout.tsx +7 -1
- package/compiler-assets/nextjs/defaults/root/theme.css +22 -22
- package/compiler-assets/nextjs/shims/generated-group-page.tsx +19 -2
- package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +16 -2
- package/compiler-assets/nextjs/shims/global-layout.tsx +11 -43
- package/compiler-assets/nextjs/shims/proxy.ts +21 -9
- package/compiler-assets/nextjs/shims/publish-os-theme-provider.tsx +76 -15
- package/compiler-assets/nextjs/shims/root-page.tsx +7 -1
- package/compiler-assets/nextjs/shims/site-fenced-code-block.tsx +34 -0
- package/compiler-assets/nextjs/shims/site-heading-level.tsx +14 -0
- package/compiler-assets/nextjs/shims/site-mdx-components.tsx +100 -0
- package/compiler-assets/nextjs/shims/site-mdx.tsx +12 -0
- package/compiler-assets/nextjs/shims/theme-defaults.css +41 -25
- package/dist/build-operation-2JAYBSKF.js +83 -0
- package/dist/button-CtzUJB8u.d.ts +11 -0
- package/dist/{button-CFdx-ohV.d.ts → button-c3WGnxIj.d.ts} +3 -3
- package/dist/chunk-535TM4A5.js +1455 -0
- package/dist/chunk-726GV2NF.js +2344 -0
- package/dist/chunk-7OGTPSKI.js +156 -0
- package/dist/chunk-AI2UULNX.js +9 -0
- package/dist/chunk-B2V22WRT.js +297 -0
- package/dist/chunk-DLDFHU7Y.js +191 -0
- package/dist/chunk-DWA3ZRGW.js +142 -0
- package/dist/chunk-GGKFQ3MA.js +621 -0
- package/dist/chunk-H6XILMZX.js +6154 -0
- package/dist/chunk-NECL5IBJ.js +318 -0
- package/dist/chunk-SVYGXXYR.js +36 -0
- package/dist/chunk-UF6ATARM.js +1100 -0
- package/dist/chunk-VO2I7MTM.js +56 -0
- package/dist/chunk-WDY4IK6Z.js +128 -0
- package/dist/chunk-WKTEO6RS.js +586 -0
- package/dist/chunk-YLTOLYWM.js +92 -0
- package/dist/compiler.d.ts +41 -7
- package/dist/compiler.js +48 -13143
- package/dist/compose-NN23UJGC.js +13 -0
- package/dist/config/createNextConfig.js +13 -0
- package/dist/dev-operation-TZDVJCN7.js +1055 -0
- package/dist/framework-selection-X4U6Y6QC.js +10 -0
- package/dist/ide-support-ZNW7ODFY.js +15 -0
- package/dist/index.d.ts +15 -3
- package/dist/kernel/components/blocks/AgentActions.js +71 -61
- package/dist/kernel/components/blocks/design-system-preview.d.ts +1 -1
- package/dist/kernel/components/blocks/design-system-preview.js +32 -13
- package/dist/kernel/components/blocks/link-playground.js +2 -2
- package/dist/kernel/components/blocks/logo-block.js +2 -2
- package/dist/kernel/components/blocks/nav-breadcrumb.d.ts +10 -2
- package/dist/kernel/components/blocks/nav-breadcrumb.js +20 -5
- package/dist/kernel/components/blocks/navbar.d.ts +2 -2
- package/dist/kernel/components/blocks/navbar.js +401 -60
- package/dist/kernel/components/blocks/navigation-progress-playground.js +4 -4
- package/dist/kernel/components/blocks/navigation-progress.js +4 -1
- package/dist/kernel/components/blocks/next-prev.d.ts +1 -1
- package/dist/kernel/components/blocks/next-prev.js +11 -23
- package/dist/kernel/components/blocks/page-list.d.ts +1 -1
- package/dist/kernel/components/blocks/page-list.js +5 -4
- package/dist/kernel/components/blocks/search.d.ts +83 -58
- package/dist/kernel/components/blocks/search.js +553 -426
- package/dist/kernel/components/blocks/sidebar-node.d.ts +2 -2
- package/dist/kernel/components/blocks/sidebar-node.js +11 -9
- package/dist/kernel/components/blocks/sidebar.d.ts +3 -3
- package/dist/kernel/components/blocks/sidebar.js +18 -11
- package/dist/kernel/components/blocks/theme-toggle.d.ts +2 -2
- package/dist/kernel/components/blocks/theme-toggle.js +48 -2
- package/dist/kernel/components/blocks/toc.js +3 -3
- package/dist/kernel/components/mdx/components/blockquote.js +1 -1
- package/dist/kernel/components/mdx/components/callout.d.ts +5 -7
- package/dist/kernel/components/mdx/components/callout.js +66 -35
- package/dist/kernel/components/mdx/components/code-block.d.ts +5 -2
- package/dist/kernel/components/mdx/components/code-block.js +106 -5
- package/dist/kernel/components/mdx/components/copy-code-button.d.ts +8 -0
- package/dist/kernel/components/mdx/components/copy-code-button.js +60 -0
- package/dist/kernel/components/mdx/components/figure-caption.js +1 -1
- package/dist/kernel/components/mdx/components/figure.js +1 -1
- package/dist/kernel/components/mdx/components/heading-copy-link.js +3 -3
- package/dist/kernel/components/mdx/components/image-zoom.d.ts +2 -2
- package/dist/kernel/components/mdx/components/image-zoom.js +1 -1
- package/dist/kernel/components/mdx/components/image.js +77 -56
- package/dist/kernel/components/mdx/components/inline-code.js +1 -1
- package/dist/kernel/components/mdx/components/kbd.d.ts +2 -2
- package/dist/kernel/components/mdx/components/kbd.js +3 -2
- package/dist/kernel/components/mdx/components/link.d.ts +35 -4
- package/dist/kernel/components/mdx/components/link.js +1 -1
- package/dist/kernel/components/mdx/components/paragraph.js +1 -4
- package/dist/kernel/components/mdx/components/task-list.js +1 -1
- package/dist/kernel/components/mdx/components/video-player.js +2 -2
- package/dist/kernel/components/mdx/components/video.js +1 -1
- package/dist/kernel/components/mdx/error-boundary.d.ts +1 -1
- package/dist/kernel/components/mdx/mdx.d.ts +12 -8
- package/dist/kernel/components/mdx/mdx.js +115 -39
- package/dist/kernel/components/mdx/rehype-heading-ids.js +1 -1
- package/dist/kernel/components/mdx/types.d.ts +1 -1
- package/dist/kernel/components/ui/breadcrumb-trail.d.ts +6 -23
- package/dist/kernel/components/ui/breadcrumb.d.ts +11 -3
- package/dist/kernel/components/ui/breadcrumb.js +6 -4
- package/dist/kernel/components/ui/button-group.d.ts +1 -1
- package/dist/kernel/components/ui/button-group.js +5 -4
- package/dist/kernel/components/ui/button.d.ts +2 -2
- package/dist/kernel/components/ui/button.js +6 -6
- package/dist/kernel/components/ui/card.d.ts +2 -2
- package/dist/kernel/components/ui/drawer.d.ts +3 -1
- package/dist/kernel/components/ui/drawer.js +5 -1
- package/dist/kernel/components/ui/input.d.ts +4 -4
- package/dist/kernel/components/ui/kbd.d.ts +1 -1
- package/dist/kernel/components/ui/kbd.js +9 -16
- package/dist/kernel/components/ui/link.d.ts +5 -3
- package/dist/kernel/components/ui/link.js +10 -9
- package/dist/kernel/components/ui/menu.js +5 -5
- package/dist/kernel/components/ui/mod-key.d.ts +10 -0
- package/dist/kernel/components/ui/mod-key.js +36 -0
- package/dist/kernel/components/ui/navigation-menu.js +2 -2
- package/dist/kernel/components/ui/progress.js +1 -1
- package/dist/kernel/components/ui/scroll-area.d.ts +8 -8
- package/dist/kernel/components/ui/scroll-area.js +4 -4
- package/dist/kernel/components/ui/select.js +7 -10
- package/dist/kernel/components/ui/text.d.ts +2 -2
- package/dist/kernel/components/ui/theme-toggle.d.ts +3 -3
- package/dist/kernel/components/ui/theme-toggle.js +4 -9
- package/dist/kernel/defaults.registry.d.ts +9 -9
- package/dist/kernel/defaults.registry.js +9 -9
- package/dist/kernel/routes/agent/markdown.d.ts +0 -1
- package/dist/kernel/routes/layouts/group.d.ts +1 -1
- package/dist/kernel/routes/metadata/feed.d.ts +1 -1
- package/dist/kernel/routes/metadata/feed.js +13 -9
- package/dist/kernel/routes/metadata/llms.d.ts +2 -1
- package/dist/kernel/routes/metadata/llms.js +4 -4
- package/dist/kernel/routes/metadata/robots.d.ts +1 -1
- package/dist/kernel/routes/metadata/robots.js +2 -2
- package/dist/kernel/routes/metadata/sitemap.d.ts +6 -3
- package/dist/kernel/routes/metadata/sitemap.js +48 -20
- package/dist/kernel/routes/pages/group.d.ts +1 -1
- package/dist/kernel/routes/pages/group.js +20 -12
- package/dist/kernel/routes/pages/leaf.d.ts +2 -2
- package/dist/kernel/routes/pages/leaf.js +20 -12
- package/dist/kernel/routes/pages/root.d.ts +2 -2
- package/dist/kernel/routes/pages/root.js +18 -12
- package/dist/resolvers/manifest-resolver.js +29 -6
- package/dist/search.d.ts +6 -7
- package/dist/search.js +47 -22
- package/dist/{sidebar-types-Bgchdfd6.d.ts → sidebar-types-DKpR27II.d.ts} +1 -1
- package/dist/{types-CIwwieNw.d.ts → types-BzkZty3r.d.ts} +23 -3
- package/dist/{types-DHBm7-j6.d.ts → types-CJWRG05y.d.ts} +20 -1
- package/dist/{types-B_b3C0U6.d.ts → types-CUtrppxt.d.ts} +8 -2
- package/dist/types.d.ts +14 -6
- package/dist/validate-local-source-XRL2Y7XE.js +14 -0
- package/dist/validate-publish-os-source-DAPZ4QGE.js +12 -0
- package/init/seed/minimal/.gitignore +1 -0
- package/init/seed/minimal/publish.os/assets/favicon/favicon.dark.png +0 -0
- package/init/seed/minimal/publish.os/assets/favicon/favicon.png +0 -0
- package/init/seed/minimal/publish.os/docs/content/get-started.mdx +6 -10
- package/init/seed/minimal/publish.os/docs/content/groups.mdx +1 -1
- package/init/seed/minimal/publish.os/docs/content/{theme.mdx → themes.mdx} +14 -12
- package/init/seed/minimal/publish.os/publish.os.config.json +3 -7
- package/init/seed/minimal/publish.os/theme.css +52 -24
- package/init/seed/minimal/publish.os/tsconfig.json +3 -0
- package/init/seed/minimal/seed.json +4 -1
- package/init/seed/scratch/publish.os/docs/content/get-started.mdx +6 -0
- package/init/seed/scratch/publish.os/publish.os.config.json +4 -0
- package/init/seed/scratch/seed.json +5 -0
- package/package.json +12 -26
- package/release/app/phosphor/package.json +3 -3
- package/release/app/phosphor/pnpm-lock.yaml.template +523 -360
- package/release/app/phosphor/pnpm-workspace.yaml +3 -0
- package/release/compiler/package.json +1 -1
- package/release/compiler/pnpm-lock.yaml.template +439 -286
- package/release/compiler/pnpm-workspace.yaml +3 -0
- package/release/release.json +7 -36
- package/compiler-assets/nextjs/defaults/group/leaf.tsx +0 -48
- package/dist/icons.d.ts +0 -1
- package/dist/icons.js +0 -2
- package/dist/kernel/components/mdx/components/pre.d.ts +0 -6
- package/dist/kernel/components/mdx/components/pre.js +0 -18
- package/dist/kernel/components/ui/meta-key.d.ts +0 -17
- package/dist/kernel/components/ui/meta-key.js +0 -9
- package/dist/kernel/framework-icons.d.ts +0 -1
- package/dist/kernel/framework-icons.js +0 -39
- package/dist/link-CYaCRDb6.d.ts +0 -35
- package/dist/route-metadata-og-image-BeKKFHg7.d.ts +0 -58
- package/dist/types-sM2g9nW0.d.ts +0 -12
- package/release/app/lucide/package.json +0 -30
- package/release/app/lucide/pnpm-lock.yaml.template +0 -2905
- package/release/app/lucide/pnpm-workspace.yaml +0 -11
- package/release/app/tabler/package.json +0 -30
- package/release/app/tabler/pnpm-lock.yaml.template +0 -2911
- package/release/app/tabler/pnpm-workspace.yaml +0 -11
|
@@ -0,0 +1,1100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createIdeSourceArtifacts,
|
|
3
|
+
materializeAppInstallation,
|
|
4
|
+
prepareAppInstallation
|
|
5
|
+
} from "./chunk-H6XILMZX.js";
|
|
6
|
+
import {
|
|
7
|
+
supportedNextjsSourceImports
|
|
8
|
+
} from "./chunk-NECL5IBJ.js";
|
|
9
|
+
import {
|
|
10
|
+
installLocalApp
|
|
11
|
+
} from "./chunk-WKTEO6RS.js";
|
|
12
|
+
import {
|
|
13
|
+
acquireOperationLock,
|
|
14
|
+
assertLocalDirectory,
|
|
15
|
+
ensureOutputOwnership
|
|
16
|
+
} from "./chunk-GGKFQ3MA.js";
|
|
17
|
+
import {
|
|
18
|
+
assertFrameworkSelection,
|
|
19
|
+
readCheckoutTypeScriptConfig
|
|
20
|
+
} from "./chunk-DLDFHU7Y.js";
|
|
21
|
+
import {
|
|
22
|
+
runtimePackageVersion
|
|
23
|
+
} from "./chunk-AI2UULNX.js";
|
|
24
|
+
import {
|
|
25
|
+
AppError
|
|
26
|
+
} from "./chunk-WDY4IK6Z.js";
|
|
27
|
+
|
|
28
|
+
// src/lib/compiler/ide/ide-support.ts
|
|
29
|
+
import { createHash, randomUUID } from "crypto";
|
|
30
|
+
import {
|
|
31
|
+
cp,
|
|
32
|
+
link,
|
|
33
|
+
lstat,
|
|
34
|
+
mkdir,
|
|
35
|
+
readdir,
|
|
36
|
+
readFile,
|
|
37
|
+
realpath,
|
|
38
|
+
rm as rm2,
|
|
39
|
+
stat,
|
|
40
|
+
writeFile
|
|
41
|
+
} from "fs/promises";
|
|
42
|
+
import { createRequire } from "module";
|
|
43
|
+
import {
|
|
44
|
+
basename as basename2,
|
|
45
|
+
dirname as dirname2,
|
|
46
|
+
isAbsolute,
|
|
47
|
+
join as join2,
|
|
48
|
+
relative,
|
|
49
|
+
resolve,
|
|
50
|
+
sep
|
|
51
|
+
} from "path";
|
|
52
|
+
import { isDeepStrictEqual } from "util";
|
|
53
|
+
import { parseConfigFileTextToJson } from "typescript";
|
|
54
|
+
|
|
55
|
+
// src/lib/compiler/ide/ide-install.ts
|
|
56
|
+
async function runIdeInstall(options) {
|
|
57
|
+
let shutdown = /* @__PURE__ */ new Set(), failure;
|
|
58
|
+
try {
|
|
59
|
+
await installLocalApp({
|
|
60
|
+
...options,
|
|
61
|
+
purpose: "ide",
|
|
62
|
+
lifetime: {
|
|
63
|
+
signal: options.options.signal ?? new AbortController().signal,
|
|
64
|
+
onShutdown: (stop) => (shutdown.add(stop), () => {
|
|
65
|
+
shutdown.delete(stop);
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
failure = error;
|
|
71
|
+
}
|
|
72
|
+
if ((await Promise.allSettled([...shutdown].map((stop) => stop()))).some((result) => result.status === "rejected"))
|
|
73
|
+
throw new AppError({
|
|
74
|
+
code: "FRAMEWORK_PROCESS_SHUTDOWN_FAILED",
|
|
75
|
+
message: "The IDE installer did not stop. Its staging files and lock remain in place.",
|
|
76
|
+
fix: "Stop and verify all installer descendants before recovering the retained IDE paths.",
|
|
77
|
+
details: { cause: failure }
|
|
78
|
+
});
|
|
79
|
+
if (failure) throw failure;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/lib/compiler/ide/ide-output.ts
|
|
83
|
+
import { mkdtemp, rename, rm, symlink, unlink } from "fs/promises";
|
|
84
|
+
import { basename, dirname, join } from "path";
|
|
85
|
+
var IDE_CLEANUP_TIMEOUT_MS = 3e4;
|
|
86
|
+
async function cleanupIdeFiles({
|
|
87
|
+
cleanup,
|
|
88
|
+
label
|
|
89
|
+
}) {
|
|
90
|
+
let timer;
|
|
91
|
+
try {
|
|
92
|
+
await Promise.race([
|
|
93
|
+
cleanup(),
|
|
94
|
+
new Promise((_resolve, reject) => {
|
|
95
|
+
timer = setTimeout(
|
|
96
|
+
() => reject(
|
|
97
|
+
new AppError({
|
|
98
|
+
code: "IDE_CLEANUP_TIMEOUT",
|
|
99
|
+
message: `${label} did not finish within ${IDE_CLEANUP_TIMEOUT_MS} ms.`,
|
|
100
|
+
fix: "Stop and verify owned processes before you recover the retained IDE files."
|
|
101
|
+
})
|
|
102
|
+
),
|
|
103
|
+
IDE_CLEANUP_TIMEOUT_MS
|
|
104
|
+
);
|
|
105
|
+
})
|
|
106
|
+
]);
|
|
107
|
+
} finally {
|
|
108
|
+
clearTimeout(timer);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function withIdeOutput({
|
|
112
|
+
outputDir,
|
|
113
|
+
assertOutputCurrent,
|
|
114
|
+
run,
|
|
115
|
+
assertReadyToCommit,
|
|
116
|
+
onCleanupError,
|
|
117
|
+
reuseDependencies
|
|
118
|
+
}) {
|
|
119
|
+
let parent = dirname(outputDir);
|
|
120
|
+
await assertOutputCurrent();
|
|
121
|
+
let stage = await mkdtemp(join(parent, `.${basename(outputDir)}.tmp-`)), stageIdentity = await assertLocalDirectory(stage), assertStageCurrent = async () => {
|
|
122
|
+
await assertOutputCurrent(), await assertLocalDirectory(stage, stageIdentity);
|
|
123
|
+
}, backup = `${stage}.previous`, previous = !1, replaced = !1, dependenciesMoved = !1, backupIdentity;
|
|
124
|
+
try {
|
|
125
|
+
reuseDependencies && await symlink(
|
|
126
|
+
join(outputDir, "node_modules"),
|
|
127
|
+
join(stage, "node_modules"),
|
|
128
|
+
process.platform === "win32" ? "junction" : "dir"
|
|
129
|
+
), await run(stage, assertStageCurrent), await assertStageCurrent(), await assertReadyToCommit(), await assertStageCurrent();
|
|
130
|
+
try {
|
|
131
|
+
await rename(outputDir, backup), previous = !0, backupIdentity = await assertLocalDirectory(backup);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (reuseDependencies || error.code !== "ENOENT")
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
await assertStageCurrent(), reuseDependencies && (await unlink(join(stage, "node_modules")), await assertLocalDirectory(backup, backupIdentity), await rename(join(backup, "node_modules"), join(stage, "node_modules")), dependenciesMoved = !0), await rename(stage, outputDir), replaced = !0;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (previous)
|
|
140
|
+
try {
|
|
141
|
+
await assertOutputCurrent(), await assertLocalDirectory(backup, backupIdentity), dependenciesMoved && await rename(
|
|
142
|
+
join(stage, "node_modules"),
|
|
143
|
+
join(backup, "node_modules")
|
|
144
|
+
), await rename(backup, outputDir);
|
|
145
|
+
} catch (rollbackError) {
|
|
146
|
+
throw new AppError({
|
|
147
|
+
code: "IDE_ROLLBACK_FAILED",
|
|
148
|
+
message: `IDE output replacement failed. Previous output remains at ${backup}.`,
|
|
149
|
+
fix: "Restore the previous IDE output after all command processes stop.",
|
|
150
|
+
details: { error, rollbackError }
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error instanceof AppError && ["FRAMEWORK_PROCESS_SHUTDOWN_FAILED", "IDE_ROLLBACK_FAILED"].includes(
|
|
157
|
+
error.code
|
|
158
|
+
))
|
|
159
|
+
throw error;
|
|
160
|
+
try {
|
|
161
|
+
await cleanupIdeFiles({
|
|
162
|
+
cleanup: async () => {
|
|
163
|
+
await assertStageCurrent(), await rm(stage, { recursive: !0, force: !0 });
|
|
164
|
+
},
|
|
165
|
+
label: "Temporary IDE output cleanup"
|
|
166
|
+
});
|
|
167
|
+
} catch (cleanupError) {
|
|
168
|
+
onCleanupError(cleanupError);
|
|
169
|
+
}
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
if (previous && replaced)
|
|
173
|
+
try {
|
|
174
|
+
await cleanupIdeFiles({
|
|
175
|
+
cleanup: async () => {
|
|
176
|
+
await assertOutputCurrent(), await assertLocalDirectory(backup, backupIdentity), await rm(backup, { recursive: !0, force: !0 });
|
|
177
|
+
},
|
|
178
|
+
label: "Previous IDE output cleanup"
|
|
179
|
+
});
|
|
180
|
+
} catch (cleanupError) {
|
|
181
|
+
onCleanupError(cleanupError);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/lib/compiler/ide/ide-support.ts
|
|
186
|
+
var IDE_SCHEMA_VERSION = 1, IDE_TSCONFIG = `${JSON.stringify(
|
|
187
|
+
{ extends: "./.publish.os/ide/tsconfig.json" },
|
|
188
|
+
null,
|
|
189
|
+
2
|
|
190
|
+
)}
|
|
191
|
+
`, IDE_LOCK_TIMEOUT_MS = 3e4, IDE_LOCK_RETRY_MS = 50, IDE_RELEASE_GRAPH_FILES = [
|
|
192
|
+
"framework.tgz",
|
|
193
|
+
"package.json",
|
|
194
|
+
"pnpm-lock.yaml",
|
|
195
|
+
"pnpm-workspace.yaml"
|
|
196
|
+
], IDE_GENERATED_FILES = [
|
|
197
|
+
...IDE_RELEASE_GRAPH_FILES,
|
|
198
|
+
"tsconfig.json",
|
|
199
|
+
"source/.publishos/media-manifest.generated.ts",
|
|
200
|
+
"source/.publishos/icons.generated.tsx"
|
|
201
|
+
], require2 = createRequire(import.meta.url);
|
|
202
|
+
function portableIdeTsconfig() {
|
|
203
|
+
return IDE_TSCONFIG;
|
|
204
|
+
}
|
|
205
|
+
function ideDir(publishOsDir) {
|
|
206
|
+
return resolve(publishOsDir, ".publish.os", "ide");
|
|
207
|
+
}
|
|
208
|
+
function posixPath(path) {
|
|
209
|
+
return path.split(sep).join("/");
|
|
210
|
+
}
|
|
211
|
+
function relativeIdePath(ideRoot, path) {
|
|
212
|
+
let value = posixPath(relative(ideRoot, path));
|
|
213
|
+
return value.startsWith(".") ? value : `./${value}`;
|
|
214
|
+
}
|
|
215
|
+
function fileHash(value) {
|
|
216
|
+
return createHash("sha256").update(value).digest("hex");
|
|
217
|
+
}
|
|
218
|
+
function isMissing(error) {
|
|
219
|
+
return typeof error == "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
220
|
+
}
|
|
221
|
+
async function readOptional(path) {
|
|
222
|
+
try {
|
|
223
|
+
return await readFile(path, "utf8");
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (isMissing(error)) return;
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function readOptionalBytes(path) {
|
|
230
|
+
try {
|
|
231
|
+
return await readFile(path);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (isMissing(error)) return;
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function exists(path) {
|
|
238
|
+
return await stat(path).then(() => !0).catch((error) => {
|
|
239
|
+
if (isMissing(error)) return !1;
|
|
240
|
+
throw error;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function sourceRuntimeVersion(runtime) {
|
|
244
|
+
let version = runtimePackageVersion(runtime);
|
|
245
|
+
if (version === void 0)
|
|
246
|
+
throw new AppError({
|
|
247
|
+
code: "IDE_SOURCE_SELECTION_INVALID",
|
|
248
|
+
fix: 'Set runtime to "@publish.os/nextjs@<exact-version>" in publish.os.config.json. Use the exact framework version for this source.',
|
|
249
|
+
message: "IDE support could not read the current runtime pin."
|
|
250
|
+
});
|
|
251
|
+
return version;
|
|
252
|
+
}
|
|
253
|
+
async function sourceSelection(publishOsDir) {
|
|
254
|
+
let document;
|
|
255
|
+
try {
|
|
256
|
+
document = JSON.parse(
|
|
257
|
+
await readFile(resolve(publishOsDir, "publish.os.config.json"), "utf8")
|
|
258
|
+
);
|
|
259
|
+
} catch {
|
|
260
|
+
throw new AppError({
|
|
261
|
+
code: "IDE_SOURCE_SELECTION_INVALID",
|
|
262
|
+
fix: "Restore publish.os.config.json, then run publish.os init again.",
|
|
263
|
+
message: "IDE support could not read the current source selection."
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return { runtimeVersion: sourceRuntimeVersion(document?.runtime) };
|
|
267
|
+
}
|
|
268
|
+
function assertSelectionCurrent(options) {
|
|
269
|
+
if (options.actual.runtimeVersion !== options.expected.runtimeVersion)
|
|
270
|
+
throw new AppError({
|
|
271
|
+
code: "IDE_SELECTION_STALE",
|
|
272
|
+
fix: "Run publish.os init again after the source change is complete.",
|
|
273
|
+
message: "The source runtime pin changed while IDE support was being prepared."
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
async function acquireIdeLock(options) {
|
|
277
|
+
let legacyLock = resolve(options.publishOsDir, ".publish.os", ".ide-lock"), deadline = Date.now() + IDE_LOCK_TIMEOUT_MS;
|
|
278
|
+
for (; ; ) {
|
|
279
|
+
options.signal.throwIfAborted(), await options.assertCurrent();
|
|
280
|
+
try {
|
|
281
|
+
if (!await lstat(legacyLock).then(
|
|
282
|
+
() => !0,
|
|
283
|
+
(error) => {
|
|
284
|
+
if (isMissing(error)) return !1;
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
))
|
|
288
|
+
return await acquireOperationLock({
|
|
289
|
+
sourceRoot: options.publishOsDir,
|
|
290
|
+
outputRoot: join2(options.publishOsDir, ".publish.os"),
|
|
291
|
+
operation: "ide",
|
|
292
|
+
signal: options.signal
|
|
293
|
+
});
|
|
294
|
+
} catch (error) {
|
|
295
|
+
if (!(error instanceof AppError)) throw error;
|
|
296
|
+
if (error.code !== "FRAMEWORK_LOCAL_OPERATION_ACTIVE")
|
|
297
|
+
throw new AppError({
|
|
298
|
+
code: "IDE_PREPARATION_PROTECTED",
|
|
299
|
+
message: `IDE preparation cannot verify the lock owner for ${options.publishOsDir}.`,
|
|
300
|
+
fix: `Inspect ${join2(options.publishOsDir, ".publish.os", ".locks", "ide")} and its recorded owner. Retry only after ownership and process shutdown can be verified; leave uncertain locks and source in place.`,
|
|
301
|
+
details: { sourceRoot: options.publishOsDir, causeCode: error.code }
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (Date.now() >= deadline)
|
|
305
|
+
throw new AppError({
|
|
306
|
+
code: "IDE_PREPARATION_BUSY",
|
|
307
|
+
fix: `Wait for the IDE preparation owner of ${options.publishOsDir} to finish, then retry. If the legacy lock ${legacyLock} remains, inspect its owner before recovering it.`,
|
|
308
|
+
message: `IDE support is already being prepared for ${options.publishOsDir}.`,
|
|
309
|
+
details: { sourceRoot: options.publishOsDir }
|
|
310
|
+
});
|
|
311
|
+
await new Promise((resolve2) => setTimeout(resolve2, IDE_LOCK_RETRY_MS)), options.signal.throwIfAborted();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function readIdeMetadata(source) {
|
|
315
|
+
if (source)
|
|
316
|
+
try {
|
|
317
|
+
let value = JSON.parse(source), files = value.files, selection = value.selection;
|
|
318
|
+
return value.schemaVersion !== IDE_SCHEMA_VERSION || !files || typeof files != "object" || Array.isArray(files) || ![
|
|
319
|
+
"tsconfig.json",
|
|
320
|
+
"source/.publishos/media-manifest.generated.ts",
|
|
321
|
+
...IDE_RELEASE_GRAPH_FILES.filter(
|
|
322
|
+
(path) => path !== "framework.tgz" || selection?.inputKind !== "checkout"
|
|
323
|
+
)
|
|
324
|
+
].every((path) => Object.hasOwn(files, path)) || Object.entries(files).some(
|
|
325
|
+
([path, hash]) => !IDE_GENERATED_FILES.includes(path) || typeof hash != "string" || !/^[a-f0-9]{64}$/.test(hash)
|
|
326
|
+
) || !selection || typeof selection.runtimeVersion != "string" || runtimePackageVersion(
|
|
327
|
+
`@publish.os/nextjs@${selection.runtimeVersion}`
|
|
328
|
+
) === void 0 || selection.inputKind !== void 0 && !["customer", "checkout", "candidate"].includes(selection.inputKind) || (selection.integrity === void 0 ? selection.inputKind !== void 0 : typeof selection.integrity != "string" || !(selection.inputKind === "checkout" ? /^checkout:[a-f0-9]{64}$/u : /^sha512-[A-Za-z0-9+/]{86}==$/u).test(selection.integrity)) ? void 0 : value;
|
|
329
|
+
} catch {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async function assertGeneratedIdeOutputUnchanged(options) {
|
|
334
|
+
let metadataSource = await readOptional(
|
|
335
|
+
join2(options.ideRoot, "metadata.json")
|
|
336
|
+
);
|
|
337
|
+
if (!metadataSource) {
|
|
338
|
+
if (await exists(options.ideRoot))
|
|
339
|
+
throw new AppError({
|
|
340
|
+
code: "IDE_OUTPUT_UNRECOGNIZED",
|
|
341
|
+
fix: `Inspect ${options.ideRoot} and preserve its custom files. Retry IDE preparation only after its generated ownership can be verified.`,
|
|
342
|
+
message: `IDE support found unrecognized output at ${options.ideRoot}.`
|
|
343
|
+
});
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
let metadata = readIdeMetadata(metadataSource);
|
|
347
|
+
if (!metadata)
|
|
348
|
+
throw new AppError({
|
|
349
|
+
code: "IDE_OUTPUT_UNRECOGNIZED",
|
|
350
|
+
fix: `Inspect and restore recognized metadata at ${join2(options.ideRoot, "metadata.json")}, then retry. Preserve custom files and source.`,
|
|
351
|
+
message: `IDE support metadata is invalid at ${options.ideRoot}.`
|
|
352
|
+
});
|
|
353
|
+
let generatedFiles = /* @__PURE__ */ new Set([...IDE_GENERATED_FILES, "metadata.json"]), generatedDirectories = /* @__PURE__ */ new Set(["source", "source/.publishos"]), inspect = async (parent) => {
|
|
354
|
+
for (let entry of await readdir(join2(options.ideRoot, parent), {
|
|
355
|
+
withFileTypes: !0
|
|
356
|
+
})) {
|
|
357
|
+
let path = parent ? `${parent}/${entry.name}` : entry.name;
|
|
358
|
+
if (!(["node_modules", ".pnpm-store"].includes(path) && entry.isDirectory()) && !(generatedFiles.has(path) && entry.isFile())) {
|
|
359
|
+
if (generatedDirectories.has(path) && entry.isDirectory()) {
|
|
360
|
+
await inspect(path);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
throw new AppError({
|
|
364
|
+
code: "IDE_OUTPUT_UNRECOGNIZED",
|
|
365
|
+
message: `IDE support found an unknown or redirected entry at ${join2(options.ideRoot, path)}.`,
|
|
366
|
+
fix: `Preserve custom files at ${options.ideRoot} and inspect their ownership before retrying IDE preparation.`
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
await inspect("");
|
|
372
|
+
let complete = !0;
|
|
373
|
+
for (let [path, expectedHash] of Object.entries(metadata.files)) {
|
|
374
|
+
let source = await readOptionalBytes(join2(options.ideRoot, path));
|
|
375
|
+
if (!source) {
|
|
376
|
+
complete = !1;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (fileHash(source) !== expectedHash)
|
|
380
|
+
throw new AppError({
|
|
381
|
+
code: "IDE_OUTPUT_MODIFIED",
|
|
382
|
+
fix: `Inspect and restore the generated files at ${options.ideRoot}, then retry IDE preparation. Preserve custom files and source.`,
|
|
383
|
+
message: `PublishOS will not replace modified IDE output at ${options.ideRoot}.`
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return { metadata, complete };
|
|
387
|
+
}
|
|
388
|
+
async function assertPortableIdeConfig(publishOsDir) {
|
|
389
|
+
let path = resolve(publishOsDir, "tsconfig.json"), source = await readOptional(path);
|
|
390
|
+
if (source === void 0) return "missing";
|
|
391
|
+
let parsed = parseConfigFileTextToJson(path, source), bases = Array.isArray(parsed.config?.extends) ? parsed.config.extends : [parsed.config?.extends];
|
|
392
|
+
if (!parsed.error && bases.some((base) => {
|
|
393
|
+
if (typeof base != "string" || !base.startsWith(".")) return !1;
|
|
394
|
+
let target = resolve(publishOsDir, base), generated = join2(ideDir(publishOsDir), "tsconfig.json");
|
|
395
|
+
return target === generated || `${target}.json` === generated;
|
|
396
|
+
}))
|
|
397
|
+
return "present";
|
|
398
|
+
throw new AppError({
|
|
399
|
+
code: "IDE_TSCONFIG_INCOMPATIBLE",
|
|
400
|
+
fix: `Set extends to "./.publish.os/ide/tsconfig.json" in ${path}, then retry IDE preparation.`,
|
|
401
|
+
message: `${path} must extend the generated IDE configuration. Custom source configuration was not replaced.`,
|
|
402
|
+
details: { sourceRoot: publishOsDir }
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
async function writePortableIdeConfig(options) {
|
|
406
|
+
let target = resolve(options.publishOsDir, "tsconfig.json"), temporary = `${target}.publishos-tmp-${process.pid}-${randomUUID()}`;
|
|
407
|
+
await options.assertCurrent(), await writeFile(temporary, portableIdeTsconfig(), { flag: "wx" });
|
|
408
|
+
try {
|
|
409
|
+
try {
|
|
410
|
+
return await options.assertCurrent(), await link(temporary, target), !0;
|
|
411
|
+
} catch (error) {
|
|
412
|
+
if (typeof error == "object" && error !== null && "code" in error && error.code === "EEXIST") {
|
|
413
|
+
if (await assertPortableIdeConfig(options.publishOsDir) === "missing")
|
|
414
|
+
throw new AppError({
|
|
415
|
+
code: "IDE_TSCONFIG_CHANGED",
|
|
416
|
+
fix: "Run publish.os init again after the source change is complete.",
|
|
417
|
+
message: "publish.os/tsconfig.json changed while IDE support was being prepared."
|
|
418
|
+
});
|
|
419
|
+
return !1;
|
|
420
|
+
}
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
423
|
+
} finally {
|
|
424
|
+
try {
|
|
425
|
+
await cleanupIdeFiles({
|
|
426
|
+
cleanup: async () => {
|
|
427
|
+
await options.assertCurrent(), await rm2(temporary, { force: !0 });
|
|
428
|
+
},
|
|
429
|
+
label: "Temporary IDE configuration cleanup"
|
|
430
|
+
});
|
|
431
|
+
} catch {
|
|
432
|
+
options.warn(
|
|
433
|
+
"Could not remove a temporary IDE configuration file. Remove publish.os/tsconfig.json.publishos-tmp-* after this command finishes."
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async function removePortableIdeConfigIfUnchanged(publishOsDir) {
|
|
439
|
+
let target = resolve(publishOsDir, "tsconfig.json");
|
|
440
|
+
await readOptional(target) === portableIdeTsconfig() && await rm2(target, { force: !0 });
|
|
441
|
+
}
|
|
442
|
+
async function assertIdeStateCurrent(options) {
|
|
443
|
+
let [rootConfig] = await Promise.all([
|
|
444
|
+
assertPortableIdeConfig(options.publishOsDir),
|
|
445
|
+
assertGeneratedIdeOutputUnchanged({ ideRoot: options.target })
|
|
446
|
+
]);
|
|
447
|
+
if (rootConfig === "missing" && options.expectedRootConfig !== "missing")
|
|
448
|
+
throw new AppError({
|
|
449
|
+
code: "IDE_TSCONFIG_CHANGED",
|
|
450
|
+
fix: "Run publish.os init again after the source change is complete.",
|
|
451
|
+
message: "publish.os/tsconfig.json changed while IDE support was being prepared."
|
|
452
|
+
});
|
|
453
|
+
if (Object.hasOwn(options, "expectedRootConfigSource") && await readOptional(join2(options.publishOsDir, "tsconfig.json")) !== options.expectedRootConfigSource)
|
|
454
|
+
throw new AppError({
|
|
455
|
+
code: "IDE_TSCONFIG_CHANGED",
|
|
456
|
+
fix: "Run publish.os init again after the source change is complete.",
|
|
457
|
+
message: "The authored TypeScript configuration changed after IDE verification."
|
|
458
|
+
});
|
|
459
|
+
assertSelectionCurrent({
|
|
460
|
+
actual: await sourceSelection(options.publishOsDir),
|
|
461
|
+
expected: options.expectedSelection
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
async function packageTypePath(options) {
|
|
465
|
+
let packageName = options.specifier.split("/").slice(0, options.specifier.startsWith("@") ? 2 : 1).join("/"), packageRoot = join2(options.ideRoot, "node_modules", packageName);
|
|
466
|
+
try {
|
|
467
|
+
JSON.parse(await readFile(join2(packageRoot, "package.json"), "utf8"));
|
|
468
|
+
} catch {
|
|
469
|
+
throw new AppError({
|
|
470
|
+
code: "IDE_PACKAGE_MANIFEST_INVALID",
|
|
471
|
+
fix: "Run publish.os init again. If this continues, update the selected framework.",
|
|
472
|
+
message: `IDE support could not read ${options.specifier} package metadata.`
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
let resolved = options.typescript.resolveModuleName(
|
|
476
|
+
options.specifier,
|
|
477
|
+
join2(options.ideRoot, "source-check.tsx"),
|
|
478
|
+
{
|
|
479
|
+
module: options.typescript.ModuleKind.ESNext,
|
|
480
|
+
moduleResolution: options.typescript.ModuleResolutionKind.Bundler,
|
|
481
|
+
preserveSymlinks: !0
|
|
482
|
+
},
|
|
483
|
+
options.typescript.sys
|
|
484
|
+
).resolvedModule;
|
|
485
|
+
if (!resolved || !/\.d\.[cm]?ts$/.test(resolved.resolvedFileName) || relativeIdePath(options.ideRoot, resolved.resolvedFileName).startsWith(
|
|
486
|
+
"../"
|
|
487
|
+
))
|
|
488
|
+
throw new AppError({
|
|
489
|
+
code: "IDE_DECLARATION_MISSING",
|
|
490
|
+
fix: "Run publish.os init again. If this continues, update the selected framework.",
|
|
491
|
+
message: `The selected framework is missing types for ${options.specifier}.`
|
|
492
|
+
});
|
|
493
|
+
return relativeIdePath(options.ideRoot, resolved.resolvedFileName);
|
|
494
|
+
}
|
|
495
|
+
async function sourceImportPaths(options) {
|
|
496
|
+
let checkoutRoot = options.checkoutRoot, paths = {}, checkoutPaths = checkoutRoot ? (await readCheckoutTypeScriptConfig({ packageRoot: checkoutRoot })).paths : void 0, typescript = typescriptApi(options.ideRoot);
|
|
497
|
+
for (let specifier of supportedNextjsSourceImports()) {
|
|
498
|
+
if (options.checkoutRoot && (specifier === "@publish.os/nextjs" || specifier.startsWith("@publish.os/nextjs/"))) {
|
|
499
|
+
let source = checkoutPaths?.[specifier]?.[0];
|
|
500
|
+
if (!source)
|
|
501
|
+
throw new AppError({
|
|
502
|
+
code: "IDE_DECLARATION_MISSING",
|
|
503
|
+
message: `The selected checkout is missing the source entry for ${specifier}.`,
|
|
504
|
+
fix: "Select a complete framework checkout and repeat IDE preparation."
|
|
505
|
+
});
|
|
506
|
+
paths[specifier] = [posixPath(join2(options.checkoutRoot, source))];
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
paths[specifier] = [
|
|
510
|
+
await packageTypePath({
|
|
511
|
+
ideRoot: options.ideRoot,
|
|
512
|
+
specifier,
|
|
513
|
+
typescript
|
|
514
|
+
})
|
|
515
|
+
];
|
|
516
|
+
}
|
|
517
|
+
if (checkoutRoot) {
|
|
518
|
+
paths["@/*"] = [posixPath(join2(checkoutRoot, "src/*"))];
|
|
519
|
+
for (let [specifier, sources] of Object.entries(checkoutPaths ?? {}))
|
|
520
|
+
specifier.startsWith("@components/") && (paths[specifier] = [
|
|
521
|
+
posixPath(
|
|
522
|
+
join2(
|
|
523
|
+
options.publishOsDir,
|
|
524
|
+
"components",
|
|
525
|
+
specifier.slice(12)
|
|
526
|
+
)
|
|
527
|
+
),
|
|
528
|
+
...sources.map((source) => posixPath(join2(checkoutRoot, source)))
|
|
529
|
+
]);
|
|
530
|
+
}
|
|
531
|
+
return paths;
|
|
532
|
+
}
|
|
533
|
+
async function ideTsconfig(options) {
|
|
534
|
+
let frameworkComponents = join2(
|
|
535
|
+
options.ideRoot,
|
|
536
|
+
"node_modules",
|
|
537
|
+
"@publish.os",
|
|
538
|
+
"nextjs",
|
|
539
|
+
"dist",
|
|
540
|
+
"kernel",
|
|
541
|
+
"components",
|
|
542
|
+
"*"
|
|
543
|
+
), paths = await sourceImportPaths({
|
|
544
|
+
ideRoot: options.ideRoot,
|
|
545
|
+
checkoutRoot: options.checkoutRoot,
|
|
546
|
+
publishOsDir: options.publishOsDir
|
|
547
|
+
});
|
|
548
|
+
return paths["@components/*"] = [
|
|
549
|
+
posixPath(resolve(options.publishOsDir, "components", "*")),
|
|
550
|
+
relativeIdePath(options.ideRoot, frameworkComponents)
|
|
551
|
+
], paths["@source/*"] = [
|
|
552
|
+
posixPath(resolve(options.publishOsDir, "*")),
|
|
553
|
+
"./source/*"
|
|
554
|
+
], `${JSON.stringify(
|
|
555
|
+
{
|
|
556
|
+
compilerOptions: {
|
|
557
|
+
allowJs: !0,
|
|
558
|
+
baseUrl: ".",
|
|
559
|
+
esModuleInterop: !0,
|
|
560
|
+
isolatedModules: !0,
|
|
561
|
+
jsx: "react-jsx",
|
|
562
|
+
lib: ["dom", "dom.iterable", "esnext"],
|
|
563
|
+
module: "esnext",
|
|
564
|
+
moduleResolution: "bundler",
|
|
565
|
+
noEmit: !0,
|
|
566
|
+
paths,
|
|
567
|
+
resolveJsonModule: !0,
|
|
568
|
+
skipLibCheck: !0,
|
|
569
|
+
strict: !0,
|
|
570
|
+
target: "ES2022",
|
|
571
|
+
typeRoots: ["./node_modules/@types"]
|
|
572
|
+
},
|
|
573
|
+
exclude: [
|
|
574
|
+
posixPath(resolve(options.publishOsDir, ".publish.os", "**/*"))
|
|
575
|
+
],
|
|
576
|
+
include: [posixPath(resolve(options.publishOsDir, "**/*"))]
|
|
577
|
+
},
|
|
578
|
+
null,
|
|
579
|
+
2
|
|
580
|
+
)}
|
|
581
|
+
`;
|
|
582
|
+
}
|
|
583
|
+
async function assertIdeDependencies(options) {
|
|
584
|
+
let required = [
|
|
585
|
+
join2(
|
|
586
|
+
options.ideRoot,
|
|
587
|
+
"node_modules",
|
|
588
|
+
"@publish.os",
|
|
589
|
+
"nextjs",
|
|
590
|
+
"package.json"
|
|
591
|
+
),
|
|
592
|
+
join2(
|
|
593
|
+
options.ideRoot,
|
|
594
|
+
"node_modules",
|
|
595
|
+
"@phosphor-icons",
|
|
596
|
+
"react",
|
|
597
|
+
"package.json"
|
|
598
|
+
),
|
|
599
|
+
join2(options.ideRoot, "node_modules", "typescript", "lib", "typescript.js"),
|
|
600
|
+
join2(options.ideRoot, "node_modules", "@types", "react", "index.d.ts")
|
|
601
|
+
];
|
|
602
|
+
if (!(await Promise.all(required.map(exists))).every(Boolean))
|
|
603
|
+
throw new AppError({
|
|
604
|
+
code: "IDE_DEPENDENCIES_MISSING",
|
|
605
|
+
fix: "Run publish.os init again. If this continues, update the selected framework.",
|
|
606
|
+
message: "The selected framework app dependencies are incomplete."
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
function typescriptApi(ideRoot) {
|
|
610
|
+
try {
|
|
611
|
+
return require2(
|
|
612
|
+
join2(ideRoot, "node_modules", "typescript", "lib", "typescript.js")
|
|
613
|
+
);
|
|
614
|
+
} catch {
|
|
615
|
+
throw new AppError({
|
|
616
|
+
code: "IDE_TYPESCRIPT_MISSING",
|
|
617
|
+
fix: "Run publish.os init again. If this continues, update the selected framework.",
|
|
618
|
+
message: "The selected framework does not provide TypeScript for IDE support."
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function assertResolvedModule(options) {
|
|
623
|
+
let file = options.typescript.resolveModuleName(
|
|
624
|
+
options.specifier,
|
|
625
|
+
join2(options.sourceRoot, "source-check.tsx"),
|
|
626
|
+
options.compilerOptions,
|
|
627
|
+
options.host
|
|
628
|
+
).resolvedModule?.resolvedFileName;
|
|
629
|
+
if (file && /\.[cm]?tsx?$/u.test(file)) {
|
|
630
|
+
let local = relative(options.ideRoot, file);
|
|
631
|
+
if (!isAbsolute(local) && local !== ".." && !local.startsWith(`..${sep}`))
|
|
632
|
+
return;
|
|
633
|
+
if (options.specifier.startsWith("@components/") || options.specifier.startsWith("@source/") || options.specifier === "@publish.os/nextjs" || options.specifier.startsWith("@publish.os/nextjs/"))
|
|
634
|
+
for (let [alias, paths] of Object.entries(
|
|
635
|
+
options.compilerOptions.paths ?? {}
|
|
636
|
+
)) {
|
|
637
|
+
let [prefix = "", suffix] = alias.split("*");
|
|
638
|
+
if (suffix === void 0 ? alias !== options.specifier : !options.specifier.startsWith(prefix) || !options.specifier.endsWith(suffix))
|
|
639
|
+
continue;
|
|
640
|
+
let wildcard = options.specifier.slice(
|
|
641
|
+
prefix.length,
|
|
642
|
+
suffix ? -suffix.length : void 0
|
|
643
|
+
);
|
|
644
|
+
for (let path of paths) {
|
|
645
|
+
let target = resolve(
|
|
646
|
+
options.compilerOptions.baseUrl ?? options.ideRoot,
|
|
647
|
+
path.replace("*", wildcard)
|
|
648
|
+
);
|
|
649
|
+
if (file === target || file.replace(/(?:\.d)?\.[cm]?tsx?$/u, "") === target || file.replace(/[/\\]index(?:\.d)?\.[cm]?tsx?$/u, "") === target)
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
throw new AppError({
|
|
655
|
+
code: "IDE_DECLARATION_UNRESOLVED",
|
|
656
|
+
fix: "Run publish.os init again. If this continues, update the selected framework.",
|
|
657
|
+
message: `IDE support could not resolve ${options.specifier}.`
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
async function verifyIdeConfiguration(options) {
|
|
661
|
+
let typescript = typescriptApi(options.ideRoot), finalIdeRoot = ideDir(options.finalPublishOsDir), rootConfigPath = join2(options.finalPublishOsDir, "tsconfig.json"), rootConfigSource = await readOptional(
|
|
662
|
+
join2(options.publishOsDir, "tsconfig.json")
|
|
663
|
+
), authoredConfig = rootConfigSource ?? portableIdeTsconfig(), replaceRoot = (path, from, to) => {
|
|
664
|
+
let suffix = relative(from, path);
|
|
665
|
+
return suffix === "" || !isAbsolute(suffix) && !suffix.startsWith(`..${sep}`) && suffix !== ".." ? join2(to, suffix) : void 0;
|
|
666
|
+
}, stagedPath = (path) => replaceRoot(path, finalIdeRoot, options.ideRoot) ?? replaceRoot(path, options.finalPublishOsDir, options.publishOsDir) ?? path, finalPath = (path) => replaceRoot(path, options.ideRoot, finalIdeRoot) ?? replaceRoot(path, options.publishOsDir, options.finalPublishOsDir) ?? path, host = {
|
|
667
|
+
...typescript.sys,
|
|
668
|
+
fileExists: (path) => path === rootConfigPath || typescript.sys.fileExists(stagedPath(path)),
|
|
669
|
+
directoryExists: (path) => typescript.sys.directoryExists(stagedPath(path)),
|
|
670
|
+
readFile: (path) => path === rootConfigPath ? authoredConfig : typescript.sys.readFile(stagedPath(path)),
|
|
671
|
+
readDirectory: (path, extensions, excludes, includes, depth) => typescript.sys.readDirectory(
|
|
672
|
+
stagedPath(path),
|
|
673
|
+
extensions,
|
|
674
|
+
excludes?.map(stagedPath),
|
|
675
|
+
includes?.map(stagedPath),
|
|
676
|
+
depth
|
|
677
|
+
).map(finalPath),
|
|
678
|
+
getDirectories: (path) => typescript.sys.getDirectories(stagedPath(path)).map(finalPath),
|
|
679
|
+
realpath: (path) => path
|
|
680
|
+
}, configPath = join2(finalIdeRoot, "tsconfig.json"), read = typescript.readConfigFile(configPath, host.readFile);
|
|
681
|
+
if (read.error || !read.config)
|
|
682
|
+
throw new AppError({
|
|
683
|
+
code: "IDE_CONFIG_INVALID",
|
|
684
|
+
fix: "Run publish.os init again. If this continues, update the selected framework.",
|
|
685
|
+
message: "PublishOS could not read the generated IDE configuration."
|
|
686
|
+
});
|
|
687
|
+
let parsed = typescript.parseJsonConfigFileContent(
|
|
688
|
+
read.config,
|
|
689
|
+
host,
|
|
690
|
+
finalIdeRoot
|
|
691
|
+
), authored = typescript.readConfigFile(rootConfigPath, host.readFile), project = typescript.parseJsonConfigFileContent(
|
|
692
|
+
authored.config,
|
|
693
|
+
host,
|
|
694
|
+
options.finalPublishOsDir
|
|
695
|
+
), incompatible = (edit) => {
|
|
696
|
+
throw new AppError({
|
|
697
|
+
code: "IDE_TSCONFIG_INCOMPATIBLE",
|
|
698
|
+
fix: `${edit} in ${rootConfigPath}, then retry IDE preparation.`,
|
|
699
|
+
message: `The authored TypeScript configuration at ${rootConfigPath} does not preserve IDE support. Custom source configuration was not replaced.`,
|
|
700
|
+
details: { sourceRoot: options.publishOsDir }
|
|
701
|
+
});
|
|
702
|
+
};
|
|
703
|
+
for (let [specifier, paths] of Object.entries(parsed.options.paths ?? {})) {
|
|
704
|
+
let expected = paths.map(
|
|
705
|
+
(path) => resolve(parsed.options.baseUrl ?? finalIdeRoot, path)
|
|
706
|
+
), actual = project.options.paths?.[specifier]?.map(
|
|
707
|
+
(path) => resolve(project.options.baseUrl ?? options.finalPublishOsDir, path)
|
|
708
|
+
);
|
|
709
|
+
if (!isDeepStrictEqual(actual, expected)) {
|
|
710
|
+
let override = Object.hasOwn(
|
|
711
|
+
authored.config.compilerOptions ?? {},
|
|
712
|
+
"paths"
|
|
713
|
+
) ? "paths" : "baseUrl";
|
|
714
|
+
incompatible(
|
|
715
|
+
`Remove the compilerOptions.${override} override to restore ${specifier}`
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (parsed.fileNames.some((path) => !project.fileNames.includes(path))) {
|
|
720
|
+
let fields = ["files", "include", "exclude"].filter(
|
|
721
|
+
(field) => Object.hasOwn(authored.config, field)
|
|
722
|
+
);
|
|
723
|
+
incompatible(
|
|
724
|
+
`Remove the ${fields.join(", ")} overrides to include all authored files`
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
let configurationErrors = project.errors.filter(
|
|
728
|
+
(diagnostic) => diagnostic.code !== 18003 || parsed.fileNames.length > 0
|
|
729
|
+
);
|
|
730
|
+
if (authored.error || configurationErrors.length > 0) {
|
|
731
|
+
let diagnostic = authored.error ?? configurationErrors[0];
|
|
732
|
+
incompatible(
|
|
733
|
+
`Correct the TypeScript configuration error: ${typescript.flattenDiagnosticMessageText(diagnostic?.messageText ?? "Invalid configuration", " ")}`
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
for (let specifier of [
|
|
737
|
+
"react",
|
|
738
|
+
"react/jsx-runtime",
|
|
739
|
+
"next/link",
|
|
740
|
+
"@components/blocks/navbar",
|
|
741
|
+
...supportedNextjsSourceImports()
|
|
742
|
+
])
|
|
743
|
+
assertResolvedModule({
|
|
744
|
+
compilerOptions: project.options,
|
|
745
|
+
host,
|
|
746
|
+
ideRoot: finalIdeRoot,
|
|
747
|
+
sourceRoot: options.finalPublishOsDir,
|
|
748
|
+
specifier,
|
|
749
|
+
typescript
|
|
750
|
+
});
|
|
751
|
+
return { rootConfigSource };
|
|
752
|
+
}
|
|
753
|
+
async function copyReleaseGraph(options) {
|
|
754
|
+
let archive = options.frameworkOptions.request.context?.archive;
|
|
755
|
+
if (!archive && options.frameworkOptions.request.context?.framework?.kind !== "checkout")
|
|
756
|
+
throw new AppError({
|
|
757
|
+
code: "IDE_RELEASE_ARCHIVE_MISSING",
|
|
758
|
+
fix: "Use the exact selected framework archive, then run publish.os init again.",
|
|
759
|
+
message: "IDE support needs the selected framework archive."
|
|
760
|
+
});
|
|
761
|
+
let plan = await prepareAppInstallation({
|
|
762
|
+
archivePath: archive?.path,
|
|
763
|
+
archiveIntegrity: archive?.integrity,
|
|
764
|
+
framework: options.frameworkOptions.request.context?.framework
|
|
765
|
+
});
|
|
766
|
+
await materializeAppInstallation({
|
|
767
|
+
plan,
|
|
768
|
+
outputDir: options.ideRoot,
|
|
769
|
+
signal: options.frameworkOptions.signal
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
async function writeIdeFiles(options) {
|
|
773
|
+
let artifacts = await createIdeSourceArtifacts({
|
|
774
|
+
mediaManifest: options.source.assets.media.manifest
|
|
775
|
+
}), config = await ideTsconfig({
|
|
776
|
+
ideRoot: options.ideRoot,
|
|
777
|
+
publishOsDir: options.finalPublishOsDir,
|
|
778
|
+
checkoutRoot: options.checkoutRoot
|
|
779
|
+
}), files = {
|
|
780
|
+
"source/.publishos/media-manifest.generated.ts": artifacts.mediaModule,
|
|
781
|
+
"tsconfig.json": config
|
|
782
|
+
};
|
|
783
|
+
return await Promise.all(
|
|
784
|
+
Object.entries(files).map(async ([path, source]) => {
|
|
785
|
+
let destination = join2(options.ideRoot, path);
|
|
786
|
+
await mkdir(dirname2(destination), { recursive: !0 }), await writeFile(destination, source);
|
|
787
|
+
})
|
|
788
|
+
), {
|
|
789
|
+
files: Object.fromEntries(
|
|
790
|
+
Object.entries(files).map(([path, source]) => [path, fileHash(source)])
|
|
791
|
+
),
|
|
792
|
+
schemaVersion: IDE_SCHEMA_VERSION,
|
|
793
|
+
selection: options.selection
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
async function releaseGraphFileHashes(ideRoot, selection) {
|
|
797
|
+
let hashes = {};
|
|
798
|
+
for (let path of IDE_RELEASE_GRAPH_FILES) {
|
|
799
|
+
if (path === "framework.tgz" && selection.inputKind === "checkout")
|
|
800
|
+
continue;
|
|
801
|
+
let source = await readOptionalBytes(join2(ideRoot, path));
|
|
802
|
+
if (!source)
|
|
803
|
+
throw new AppError({
|
|
804
|
+
code: "IDE_RELEASE_GRAPH_INVALID",
|
|
805
|
+
fix: "Run publish.os init again. If this continues, update the selected framework.",
|
|
806
|
+
message: `IDE support is missing generated ${path}.`
|
|
807
|
+
});
|
|
808
|
+
hashes[path] = fileHash(source);
|
|
809
|
+
}
|
|
810
|
+
return hashes;
|
|
811
|
+
}
|
|
812
|
+
async function copyExistingIdeOutput(options) {
|
|
813
|
+
let entries = (await readdir(options.ideRoot)).filter(
|
|
814
|
+
(entry) => entry !== "node_modules"
|
|
815
|
+
);
|
|
816
|
+
await Promise.all(
|
|
817
|
+
entries.map(
|
|
818
|
+
async (entry) => await cp(join2(options.ideRoot, entry), join2(options.stageRoot, entry), {
|
|
819
|
+
force: !0,
|
|
820
|
+
recursive: !0
|
|
821
|
+
})
|
|
822
|
+
)
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
async function canonicalSourceLocation(path) {
|
|
826
|
+
let parent = dirname2(path), canonicalParent = await realpath(parent).catch(
|
|
827
|
+
async (error) => {
|
|
828
|
+
if (error.code !== "ENOENT") throw error;
|
|
829
|
+
return await canonicalSourceLocation(parent);
|
|
830
|
+
}
|
|
831
|
+
);
|
|
832
|
+
return join2(canonicalParent, basename2(path));
|
|
833
|
+
}
|
|
834
|
+
async function prepareIdeSupport(input) {
|
|
835
|
+
let requestedFinalLocation = input.finalPublishOsDir ?? input.publishOsDir;
|
|
836
|
+
if (!isAbsolute(requestedFinalLocation) || basename2(requestedFinalLocation) !== "publish.os" || requestedFinalLocation.split(sep).some((part) => [".publish.os", "node_modules", ".git"].includes(part)))
|
|
837
|
+
throw new AppError({
|
|
838
|
+
code: "IDE_FINAL_SOURCE_INVALID",
|
|
839
|
+
fix: "Choose an absolute source path whose final folder is publish.os, outside generated or dependency folders.",
|
|
840
|
+
message: "IDE support received an invalid final source location."
|
|
841
|
+
});
|
|
842
|
+
let options = {
|
|
843
|
+
...input,
|
|
844
|
+
publishOsDir: await canonicalSourceLocation(input.publishOsDir)
|
|
845
|
+
}, finalPublishOsDir = await canonicalSourceLocation(
|
|
846
|
+
requestedFinalLocation
|
|
847
|
+
), authoredSelection = await sourceSelection(options.publishOsDir);
|
|
848
|
+
typeof options.source != "function" && assertSelectionCurrent({
|
|
849
|
+
actual: authoredSelection,
|
|
850
|
+
expected: {
|
|
851
|
+
runtimeVersion: sourceRuntimeVersion(options.source.config.runtime)
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
let identity = await assertFrameworkSelection({
|
|
855
|
+
context: options.frameworkOptions.request.context,
|
|
856
|
+
framework: options.framework,
|
|
857
|
+
runtime: `@publish.os/nextjs@${authoredSelection.runtimeVersion}`
|
|
858
|
+
}), selection = {
|
|
859
|
+
runtimeVersion: options.framework.version,
|
|
860
|
+
inputKind: options.frameworkOptions.request.context?.framework?.kind ?? "customer",
|
|
861
|
+
integrity: identity
|
|
862
|
+
}, signal = options.frameworkOptions.signal ?? new AbortController().signal, throwIfInterrupted = () => signal.throwIfAborted(), warn = (message) => options.frameworkOptions.onEvent?.({
|
|
863
|
+
type: "diagnostics",
|
|
864
|
+
diagnostics: {
|
|
865
|
+
diagnostics: [
|
|
866
|
+
{ code: "IDE_CLEANUP_WARNING", severity: "warning", message }
|
|
867
|
+
]
|
|
868
|
+
}
|
|
869
|
+
}), releaseLock, lockIdentity, releaseLockError, retainInstallerState = !1, stagedIdeDirectory, ownership = await ensureOutputOwnership({
|
|
870
|
+
sourceRoot: options.publishOsDir,
|
|
871
|
+
ownerSourceRoot: finalPublishOsDir
|
|
872
|
+
}).catch((error) => {
|
|
873
|
+
throw !(error instanceof AppError) || ![
|
|
874
|
+
"FRAMEWORK_LOCAL_OUTPUT_OWNERSHIP",
|
|
875
|
+
"FRAMEWORK_LOCAL_OUTPUT_REDIRECTED"
|
|
876
|
+
].includes(error.code) ? error : new AppError({
|
|
877
|
+
code: "IDE_PREPARATION_PROTECTED",
|
|
878
|
+
message: `IDE preparation cannot verify source-owned output for ${options.publishOsDir}.`,
|
|
879
|
+
fix: `Inspect ${join2(options.publishOsDir, ".publish.os")} ownership, including ${join2(options.publishOsDir, ".publish.os", ".ide-lock")} and ${join2(options.publishOsDir, ".publish.os", ".locks", "ide")}. Preserve redirected or uncertain entries until their ownership can be verified, then retry.`,
|
|
880
|
+
details: { sourceRoot: options.publishOsDir, causeCode: error.code }
|
|
881
|
+
});
|
|
882
|
+
}), lockDirectory = join2(ownership.outputRoot, ".locks", "ide");
|
|
883
|
+
try {
|
|
884
|
+
releaseLock = await acquireIdeLock({
|
|
885
|
+
assertCurrent: ownership.assertCurrent,
|
|
886
|
+
publishOsDir: options.publishOsDir,
|
|
887
|
+
signal
|
|
888
|
+
}), lockIdentity = await assertLocalDirectory(lockDirectory), throwIfInterrupted(), await ownership.assertCurrent();
|
|
889
|
+
let target = ideDir(options.publishOsDir), targetIdentity = await assertLocalDirectory(target).catch(
|
|
890
|
+
(error) => {
|
|
891
|
+
if (error.code !== "ENOENT") throw error;
|
|
892
|
+
}
|
|
893
|
+
), assertTargetCurrent = async () => {
|
|
894
|
+
await ownership.assertCurrent(), targetIdentity && await assertLocalDirectory(target, targetIdentity);
|
|
895
|
+
}, [existingOutput, rootConfig] = await Promise.all([
|
|
896
|
+
assertGeneratedIdeOutputUnchanged({ ideRoot: target }),
|
|
897
|
+
assertPortableIdeConfig(options.publishOsDir)
|
|
898
|
+
]), existingMetadata = existingOutput?.metadata, dependenciesReady = existingMetadata ? await assertIdeDependencies({ ideRoot: target }).then(
|
|
899
|
+
() => !0,
|
|
900
|
+
(error) => {
|
|
901
|
+
if (error instanceof AppError && error.code === "IDE_DEPENDENCIES_MISSING")
|
|
902
|
+
return !1;
|
|
903
|
+
throw error;
|
|
904
|
+
}
|
|
905
|
+
) : !1, preserve = options.frameworkOptions.request.context?.ide?.policy === "preserve" && existingOutput?.complete === !0 && dependenciesReady, checkoutGraphUnchanged = !1;
|
|
906
|
+
if (!preserve && selection.inputKind === "checkout" && existingMetadata?.selection.inputKind === "checkout") {
|
|
907
|
+
let plan = await prepareAppInstallation({
|
|
908
|
+
framework: options.frameworkOptions.request.context?.framework
|
|
909
|
+
});
|
|
910
|
+
checkoutGraphUnchanged = (await Promise.all(
|
|
911
|
+
[
|
|
912
|
+
["package.json", plan.files.packageJsonSource],
|
|
913
|
+
["pnpm-workspace.yaml", plan.files.pnpmWorkspaceSource],
|
|
914
|
+
["pnpm-lock.yaml", plan.files.lockfileSource]
|
|
915
|
+
].map(
|
|
916
|
+
async ([path, contents]) => await readOptional(join2(target, path)) === contents
|
|
917
|
+
)
|
|
918
|
+
)).every(Boolean);
|
|
919
|
+
}
|
|
920
|
+
let reusable = existingOutput?.complete === !0 && existingMetadata?.selection.runtimeVersion === selection.runtimeVersion && existingMetadata.selection.inputKind === selection.inputKind && (existingMetadata.selection.integrity === selection.integrity || checkoutGraphUnchanged) && !Object.keys(existingMetadata.files).some(
|
|
921
|
+
(path) => path.endsWith("icons.generated.tsx")
|
|
922
|
+
) && dependenciesReady;
|
|
923
|
+
if (preserve || reusable) {
|
|
924
|
+
let verified = await verifyIdeConfiguration({
|
|
925
|
+
ideRoot: target,
|
|
926
|
+
finalPublishOsDir,
|
|
927
|
+
publishOsDir: options.publishOsDir
|
|
928
|
+
}).catch((error) => {
|
|
929
|
+
if (error instanceof AppError && error.code === "IDE_DECLARATION_UNRESOLVED") {
|
|
930
|
+
reusable = !1;
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
throw error;
|
|
934
|
+
});
|
|
935
|
+
if (verified && rootConfig === "present" && (preserve || typeof options.source == "function")) {
|
|
936
|
+
await assertTargetCurrent(), await assertIdeStateCurrent({
|
|
937
|
+
expectedRootConfig: rootConfig,
|
|
938
|
+
expectedRootConfigSource: verified.rootConfigSource,
|
|
939
|
+
expectedSelection: authoredSelection,
|
|
940
|
+
publishOsDir: options.publishOsDir,
|
|
941
|
+
target
|
|
942
|
+
}), throwIfInterrupted();
|
|
943
|
+
let release = releaseLock;
|
|
944
|
+
releaseLock = void 0, await cleanupIdeFiles({ cleanup: release, label: "IDE lock cleanup" });
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
let source = typeof options.source == "function" ? await options.source() : options.source;
|
|
949
|
+
assertSelectionCurrent({
|
|
950
|
+
actual: await sourceSelection(options.publishOsDir),
|
|
951
|
+
expected: { runtimeVersion: sourceRuntimeVersion(source.config.runtime) }
|
|
952
|
+
}), reusable || options.frameworkOptions.onEvent?.({
|
|
953
|
+
type: "progress",
|
|
954
|
+
message: "Installing the selected framework app dependencies for IDE support..."
|
|
955
|
+
});
|
|
956
|
+
let wroteRootConfig = !1;
|
|
957
|
+
try {
|
|
958
|
+
await assertPortableIdeConfig(options.publishOsDir);
|
|
959
|
+
let stagedMetadata, verifiedConfiguration;
|
|
960
|
+
await withIdeOutput({
|
|
961
|
+
assertOutputCurrent: ownership.assertCurrent,
|
|
962
|
+
onCleanupError: () => {
|
|
963
|
+
warn(
|
|
964
|
+
"Could not remove temporary IDE output. The original error remains the command error."
|
|
965
|
+
);
|
|
966
|
+
},
|
|
967
|
+
outputDir: target,
|
|
968
|
+
reuseDependencies: reusable,
|
|
969
|
+
run: async (stageRoot, assertStageCurrent) => {
|
|
970
|
+
if (stagedIdeDirectory = stageRoot, await assertStageCurrent(), await assertTargetCurrent(), reusable)
|
|
971
|
+
await copyExistingIdeOutput({ ideRoot: target, stageRoot });
|
|
972
|
+
else {
|
|
973
|
+
await copyReleaseGraph({
|
|
974
|
+
ideRoot: stageRoot,
|
|
975
|
+
frameworkOptions: options.frameworkOptions,
|
|
976
|
+
selection
|
|
977
|
+
});
|
|
978
|
+
let expectedGraph = await releaseGraphFileHashes(
|
|
979
|
+
stageRoot,
|
|
980
|
+
selection
|
|
981
|
+
), assertInstallGraph = async () => {
|
|
982
|
+
if (await assertStageCurrent(), await assertTargetCurrent(), !isDeepStrictEqual(
|
|
983
|
+
await releaseGraphFileHashes(stageRoot, selection),
|
|
984
|
+
expectedGraph
|
|
985
|
+
))
|
|
986
|
+
throw new AppError({
|
|
987
|
+
code: "IDE_INSTALL_GRAPH_CHANGED",
|
|
988
|
+
message: "IDE installation changed the released dependency graph.",
|
|
989
|
+
fix: "Use the unchanged selected framework release. IDE output was not replaced."
|
|
990
|
+
});
|
|
991
|
+
};
|
|
992
|
+
await runIdeInstall({
|
|
993
|
+
outputDir: stageRoot,
|
|
994
|
+
framework: options.framework,
|
|
995
|
+
options: options.frameworkOptions,
|
|
996
|
+
assertInstallGraph: async () => {
|
|
997
|
+
await assertInstallGraph(), await assertPortableIdeConfig(options.publishOsDir);
|
|
998
|
+
}
|
|
999
|
+
}), throwIfInterrupted(), await assertInstallGraph();
|
|
1000
|
+
}
|
|
1001
|
+
await assertStageCurrent(), await assertTargetCurrent(), await assertIdeDependencies({
|
|
1002
|
+
ideRoot: stageRoot
|
|
1003
|
+
}), await assertStageCurrent(), stagedMetadata = await writeIdeFiles({
|
|
1004
|
+
finalPublishOsDir,
|
|
1005
|
+
ideRoot: stageRoot,
|
|
1006
|
+
publishOsDir: options.publishOsDir,
|
|
1007
|
+
selection,
|
|
1008
|
+
source,
|
|
1009
|
+
checkoutRoot: options.frameworkOptions.request.context?.framework?.kind === "checkout" ? options.framework.packageRoot : void 0
|
|
1010
|
+
}), await assertStageCurrent(), stagedMetadata.files = {
|
|
1011
|
+
...await releaseGraphFileHashes(stageRoot, selection),
|
|
1012
|
+
...stagedMetadata.files
|
|
1013
|
+
}, verifiedConfiguration = await verifyIdeConfiguration({
|
|
1014
|
+
ideRoot: stageRoot,
|
|
1015
|
+
finalPublishOsDir,
|
|
1016
|
+
publishOsDir: options.publishOsDir
|
|
1017
|
+
}), await assertStageCurrent(), await assertTargetCurrent(), await assertIdeStateCurrent({
|
|
1018
|
+
expectedRootConfig: rootConfig,
|
|
1019
|
+
expectedRootConfigSource: verifiedConfiguration.rootConfigSource,
|
|
1020
|
+
expectedSelection: authoredSelection,
|
|
1021
|
+
publishOsDir: options.publishOsDir,
|
|
1022
|
+
target
|
|
1023
|
+
}), throwIfInterrupted(), await assertStageCurrent(), await writeFile(
|
|
1024
|
+
join2(stageRoot, "metadata.json"),
|
|
1025
|
+
`${JSON.stringify(stagedMetadata, null, 2)}
|
|
1026
|
+
`
|
|
1027
|
+
);
|
|
1028
|
+
},
|
|
1029
|
+
assertReadyToCommit: async () => {
|
|
1030
|
+
await assertTargetCurrent(), await assertIdeStateCurrent({
|
|
1031
|
+
expectedRootConfig: rootConfig,
|
|
1032
|
+
expectedRootConfigSource: verifiedConfiguration?.rootConfigSource,
|
|
1033
|
+
expectedSelection: authoredSelection,
|
|
1034
|
+
publishOsDir: options.publishOsDir,
|
|
1035
|
+
target
|
|
1036
|
+
}), throwIfInterrupted(), rootConfig === "missing" && (wroteRootConfig = await writePortableIdeConfig({
|
|
1037
|
+
assertCurrent: ownership.assertCurrent,
|
|
1038
|
+
publishOsDir: options.publishOsDir,
|
|
1039
|
+
warn
|
|
1040
|
+
})), await assertTargetCurrent(), await assertIdeStateCurrent({
|
|
1041
|
+
expectedRootConfigSource: wroteRootConfig ? portableIdeTsconfig() : verifiedConfiguration?.rootConfigSource,
|
|
1042
|
+
expectedSelection: authoredSelection,
|
|
1043
|
+
publishOsDir: options.publishOsDir,
|
|
1044
|
+
target
|
|
1045
|
+
}), throwIfInterrupted();
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
if (isShutdownFailure(error)) {
|
|
1050
|
+
retainInstallerState = !0;
|
|
1051
|
+
try {
|
|
1052
|
+
await ownership.assertCurrent(), await assertLocalDirectory(lockDirectory, lockIdentity), await writeFile(
|
|
1053
|
+
join2(lockDirectory, "shutdown-unverified.json"),
|
|
1054
|
+
`${JSON.stringify({ stage: stagedIdeDirectory })}
|
|
1055
|
+
`,
|
|
1056
|
+
{ flag: "wx" }
|
|
1057
|
+
);
|
|
1058
|
+
} catch {
|
|
1059
|
+
warn(
|
|
1060
|
+
"Could not mark the retained IDE lock as unverified. Inspect its installer descendants before recovering it."
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
throw warn(
|
|
1064
|
+
`Could not confirm that the IDE installer stopped. Temporary files remain at ${stagedIdeDirectory}. The lock remains at ${lockDirectory}. Stop the installer and its child processes before you inspect these paths.`
|
|
1065
|
+
), error;
|
|
1066
|
+
}
|
|
1067
|
+
if (wroteRootConfig)
|
|
1068
|
+
try {
|
|
1069
|
+
await cleanupIdeFiles({
|
|
1070
|
+
cleanup: async () => {
|
|
1071
|
+
await ownership.assertCurrent(), await removePortableIdeConfigIfUnchanged(options.publishOsDir);
|
|
1072
|
+
},
|
|
1073
|
+
label: "Generated IDE configuration rollback"
|
|
1074
|
+
});
|
|
1075
|
+
} catch {
|
|
1076
|
+
warn(
|
|
1077
|
+
"Could not remove generated TypeScript configuration after IDE preparation failed. The original error remains the command error."
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
throw error;
|
|
1081
|
+
}
|
|
1082
|
+
} finally {
|
|
1083
|
+
try {
|
|
1084
|
+
releaseLock && !retainInstallerState && await cleanupIdeFiles({
|
|
1085
|
+
cleanup: releaseLock,
|
|
1086
|
+
label: "IDE lock cleanup"
|
|
1087
|
+
});
|
|
1088
|
+
} catch (error) {
|
|
1089
|
+
releaseLockError = error;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (releaseLockError) throw releaseLockError;
|
|
1093
|
+
}
|
|
1094
|
+
function isShutdownFailure(error) {
|
|
1095
|
+
return error instanceof AppError && error.code === "FRAMEWORK_PROCESS_SHUTDOWN_FAILED";
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
export {
|
|
1099
|
+
prepareIdeSupport
|
|
1100
|
+
};
|