@mintlify/package-builder 0.0.0-stage → 0.0.3

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 (46) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +66 -2
  3. package/dist/build.d.ts +4 -0
  4. package/dist/build.js +338 -0
  5. package/dist/bundle.d.ts +44 -0
  6. package/dist/bundle.js +681 -0
  7. package/dist/cli.d.ts +13 -0
  8. package/dist/cli.js +112 -0
  9. package/dist/diagnostics.d.ts +11 -0
  10. package/dist/diagnostics.js +32 -0
  11. package/dist/hashing.d.ts +28 -0
  12. package/dist/hashing.js +98 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.js +11 -0
  15. package/dist/install.d.ts +34 -0
  16. package/dist/install.js +325 -0
  17. package/dist/local-files.d.ts +11 -0
  18. package/dist/local-files.js +120 -0
  19. package/dist/lockfile.d.ts +40 -0
  20. package/dist/lockfile.js +498 -0
  21. package/dist/package-json.d.ts +14 -0
  22. package/dist/package-json.js +170 -0
  23. package/dist/package-manager.d.ts +17 -0
  24. package/dist/package-manager.js +87 -0
  25. package/dist/react.d.ts +37 -0
  26. package/dist/react.js +264 -0
  27. package/dist/render.d.ts +37 -0
  28. package/dist/render.js +292 -0
  29. package/dist/runtime/client.d.ts +11 -0
  30. package/dist/runtime/client.js +54 -0
  31. package/dist/runtime/server.d.ts +6 -0
  32. package/dist/runtime/server.js +11 -0
  33. package/dist/runtime/shell.d.ts +21 -0
  34. package/dist/runtime/shell.js +68 -0
  35. package/dist/runtime/theme.d.ts +1 -0
  36. package/dist/runtime/theme.js +1 -0
  37. package/dist/sanitize.d.ts +9 -0
  38. package/dist/sanitize.js +650 -0
  39. package/dist/semver.d.ts +2 -0
  40. package/dist/semver.js +10 -0
  41. package/dist/tsconfig.build.tsbuildinfo +1 -0
  42. package/dist/types.d.ts +98 -0
  43. package/dist/types.js +1 -0
  44. package/dist/version.d.ts +1 -0
  45. package/dist/version.js +11 -0
  46. package/package.json +74 -4
@@ -0,0 +1,170 @@
1
+ import { lstatSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import semver from 'semver';
4
+ export const DEPENDENCY_FIELDS = [
5
+ 'dependencies',
6
+ 'optionalDependencies',
7
+ 'devDependencies',
8
+ 'peerDependencies',
9
+ ];
10
+ const STRIPPED_FIELDS = ['packageManager', 'devEngines'];
11
+ const DIST_TAG = /^[a-z][a-z0-9._-]*$/i;
12
+ const OVERRIDE_REFERENCE = /^\$[@a-z0-9][a-z0-9._/@-]*$/i;
13
+ function isRecord(value) {
14
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
15
+ }
16
+ export function jsonErrorPosition(err, content) {
17
+ const match = err instanceof Error ? /position (\d+)/.exec(err.message) : null;
18
+ if (!match?.[1])
19
+ return '';
20
+ const before = content.slice(0, Number(match[1]));
21
+ const line = before.split('\n').length;
22
+ const column = before.length - before.lastIndexOf('\n');
23
+ return ` at line ${line}, column ${column}`;
24
+ }
25
+ export function readPackageJson(docsRoot, { allowSymlink }) {
26
+ const path = join(docsRoot, 'package.json');
27
+ let isSymlink;
28
+ try {
29
+ isSymlink = lstatSync(path).isSymbolicLink();
30
+ }
31
+ catch {
32
+ return {
33
+ diagnostics: [
34
+ {
35
+ level: 'error',
36
+ code: 'package-json-missing',
37
+ message: `No package.json in ${docsRoot}.`,
38
+ },
39
+ ],
40
+ };
41
+ }
42
+ if (isSymlink && !allowSymlink) {
43
+ return {
44
+ diagnostics: [
45
+ {
46
+ level: 'error',
47
+ code: 'package-json-symlink',
48
+ message: 'package.json is a symbolic link. Commit the file itself in the docs folder.',
49
+ },
50
+ ],
51
+ };
52
+ }
53
+ const content = readFileSync(path, 'utf8');
54
+ let parsed;
55
+ try {
56
+ parsed = JSON.parse(content);
57
+ }
58
+ catch (err) {
59
+ return {
60
+ diagnostics: [
61
+ {
62
+ level: 'error',
63
+ code: 'package-json-invalid',
64
+ message: `package.json is not valid JSON${jsonErrorPosition(err, content)}.`,
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ if (!isRecord(parsed)) {
70
+ return {
71
+ diagnostics: [
72
+ { level: 'error', code: 'package-json-invalid', message: 'package.json is not an object.' },
73
+ ],
74
+ };
75
+ }
76
+ return { packageJson: parsed, diagnostics: [] };
77
+ }
78
+ export function dependencySpecs(packageJson, field) {
79
+ const deps = packageJson[field];
80
+ const out = {};
81
+ if (!isRecord(deps))
82
+ return out;
83
+ for (const [name, spec] of Object.entries(deps))
84
+ if (typeof spec === 'string')
85
+ out[name] = spec;
86
+ return out;
87
+ }
88
+ export function specProblem(spec) {
89
+ const s = spec.trim();
90
+ if (s.toLowerCase().startsWith('npm:'))
91
+ return 'is an npm alias, which is not supported';
92
+ if (semver.validRange(s) !== null)
93
+ return undefined;
94
+ if (DIST_TAG.test(s))
95
+ return undefined;
96
+ return 'is not a version or version range from a registry';
97
+ }
98
+ function specError(where, name, spec, problem) {
99
+ return {
100
+ level: 'error',
101
+ code: 'package-spec-unsupported',
102
+ message: `${where} ${name} "${spec}" ${problem}. Only packages published to npm or a private registry are supported.`,
103
+ pkg: name,
104
+ };
105
+ }
106
+ function overrideProblems(value, path, out) {
107
+ if (typeof value === 'string') {
108
+ const problem = OVERRIDE_REFERENCE.test(value) ? undefined : specProblem(value);
109
+ if (problem)
110
+ out.push(specError('overrides', path, value, problem));
111
+ return;
112
+ }
113
+ if (!isRecord(value))
114
+ return;
115
+ for (const [key, nested] of Object.entries(value)) {
116
+ overrideProblems(nested, key === '.' ? path : `${path} > ${key}`, out);
117
+ }
118
+ }
119
+ export function validatePackageJson(packageJson) {
120
+ const out = [];
121
+ for (const field of DEPENDENCY_FIELDS) {
122
+ for (const [name, spec] of Object.entries(dependencySpecs(packageJson, field))) {
123
+ const problem = specProblem(spec);
124
+ if (problem)
125
+ out.push(specError(field, name, spec, problem));
126
+ }
127
+ }
128
+ if (isRecord(packageJson.overrides)) {
129
+ for (const [name, value] of Object.entries(packageJson.overrides)) {
130
+ overrideProblems(value, name, out);
131
+ }
132
+ }
133
+ const pnpm = packageJson.pnpm;
134
+ if (isRecord(pnpm)) {
135
+ if (pnpm.patchedDependencies !== undefined) {
136
+ out.push({
137
+ level: 'error',
138
+ code: 'package-patches-unsupported',
139
+ message: 'pnpm.patchedDependencies is not supported. Publish a patched version instead.',
140
+ });
141
+ }
142
+ if (isRecord(pnpm.overrides)) {
143
+ for (const [name, value] of Object.entries(pnpm.overrides)) {
144
+ overrideProblems(value, `pnpm.overrides ${name}`, out);
145
+ }
146
+ }
147
+ if (isRecord(pnpm.packageExtensions)) {
148
+ for (const [target, extension] of Object.entries(pnpm.packageExtensions)) {
149
+ if (!isRecord(extension))
150
+ continue;
151
+ for (const field of ['dependencies', 'optionalDependencies', 'peerDependencies']) {
152
+ for (const [name, spec] of Object.entries(dependencySpecs(extension, field))) {
153
+ const problem = specProblem(spec);
154
+ if (problem)
155
+ out.push(specError(`pnpm.packageExtensions ${target}`, name, spec, problem));
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ return out;
162
+ }
163
+ export function cleanPackageJson(packageJson) {
164
+ const cleaned = {};
165
+ for (const [key, value] of Object.entries(packageJson)) {
166
+ if (!STRIPPED_FIELDS.includes(key))
167
+ cleaned[key] = value;
168
+ }
169
+ return `${JSON.stringify(cleaned, null, 2)}\n`;
170
+ }
@@ -0,0 +1,17 @@
1
+ export type PackageManager = 'npm' | 'pnpm';
2
+ export type SpawnHost = {
3
+ platform: NodeJS.Platform;
4
+ execPath: string;
5
+ env: NodeJS.ProcessEnv;
6
+ isFile(path: string): boolean;
7
+ };
8
+ export type PackageManagerCommand = {
9
+ ok: true;
10
+ command: string;
11
+ args: string[];
12
+ } | {
13
+ ok: false;
14
+ message: string;
15
+ };
16
+ export declare function currentHost(): SpawnHost;
17
+ export declare function resolvePackageManager(pm: PackageManager, host?: SpawnHost): PackageManagerCommand;
@@ -0,0 +1,87 @@
1
+ import { statSync } from 'node:fs';
2
+ import { win32 } from 'node:path';
3
+ function isFile(path) {
4
+ try {
5
+ return statSync(path).isFile();
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ }
11
+ export function currentHost() {
12
+ return { platform: process.platform, execPath: process.execPath, env: process.env, isFile };
13
+ }
14
+ const EXEC_PATH_ENTRY = {
15
+ npm: /^npm-cli\.js$/i,
16
+ pnpm: /^pnpm\.(c?js|mjs|exe)$/i,
17
+ };
18
+ const NPM_CLI = 'node_modules/npm/bin/npm-cli.js';
19
+ const SHIM_TARGETS = {
20
+ npm: [NPM_CLI],
21
+ pnpm: [
22
+ 'node_modules/pnpm/pnpm.exe',
23
+ 'node_modules/@pnpm/exe/pnpm.exe',
24
+ 'node_modules/pnpm/bin/pnpm.cjs',
25
+ 'node_modules/pnpm/bin/pnpm.mjs',
26
+ 'node_modules/corepack/dist/pnpm.js',
27
+ ],
28
+ };
29
+ function shimRoot(dir) {
30
+ const parent = win32.dirname(dir);
31
+ const inBin = win32.basename(dir).toLowerCase() === '.bin' &&
32
+ win32.basename(parent).toLowerCase() === 'node_modules';
33
+ return inBin ? win32.dirname(parent) : dir;
34
+ }
35
+ function envValue(env, name) {
36
+ const key = Object.keys(env).find((k) => k.toUpperCase() === name.toUpperCase());
37
+ return key === undefined ? undefined : env[key];
38
+ }
39
+ function entryCommand(host, entry) {
40
+ return /\.exe$/i.test(entry)
41
+ ? { ok: true, command: entry, args: [] }
42
+ : { ok: true, command: host.execPath, args: [entry] };
43
+ }
44
+ function resolveOnWindows(pm, host) {
45
+ const execPathEntry = envValue(host.env, 'npm_execpath');
46
+ if (execPathEntry &&
47
+ win32.isAbsolute(execPathEntry) &&
48
+ EXEC_PATH_ENTRY[pm].test(win32.basename(execPathEntry)) &&
49
+ host.isFile(execPathEntry)) {
50
+ return entryCommand(host, execPathEntry);
51
+ }
52
+ const dirs = (envValue(host.env, 'PATH') ?? '')
53
+ .split(';')
54
+ .map((dir) => dir.trim().replace(/^"(.*)"$/, '$1'))
55
+ .filter((dir) => dir && win32.isAbsolute(dir));
56
+ for (const dir of dirs) {
57
+ const exe = win32.join(dir, `${pm}.exe`);
58
+ if (host.isFile(exe))
59
+ return { ok: true, command: exe, args: [] };
60
+ if (!host.isFile(win32.join(dir, `${pm}.cmd`)))
61
+ continue;
62
+ const root = shimRoot(dir);
63
+ for (const target of SHIM_TARGETS[pm]) {
64
+ const entry = win32.join(root, target);
65
+ if (host.isFile(entry))
66
+ return entryCommand(host, entry);
67
+ }
68
+ }
69
+ if (pm === 'npm') {
70
+ const bundled = win32.join(win32.dirname(host.execPath), NPM_CLI);
71
+ if (host.isFile(bundled))
72
+ return entryCommand(host, bundled);
73
+ return {
74
+ ok: false,
75
+ message: `Could not find npm. On Windows npm runs as node npm-cli.js, without a shell, and no npm-cli.js was found from PATH or next to ${host.execPath}.`,
76
+ };
77
+ }
78
+ return {
79
+ ok: false,
80
+ message: 'Could not find pnpm. On Windows pnpm runs without a shell, so it needs pnpm.exe on PATH or a pnpm.cmd from npm, Corepack or node_modules/.bin next to its pnpm.exe, pnpm.cjs or pnpm.mjs.',
81
+ };
82
+ }
83
+ export function resolvePackageManager(pm, host = currentHost()) {
84
+ if (host.platform !== 'win32')
85
+ return { ok: true, command: pm, args: [] };
86
+ return resolveOnWindows(pm, host);
87
+ }
@@ -0,0 +1,37 @@
1
+ import type { Metafile, Plugin } from 'esbuild';
2
+ import type { BuildDiagnostic } from './types.js';
3
+ export declare const SKIP_PLUGINS = "mint-skip";
4
+ export type ReactInfo = {
5
+ version: string;
6
+ domVersion: string;
7
+ reactDir: string;
8
+ reactDomDir: string;
9
+ };
10
+ export declare const REACT_SPECIFIER: RegExp;
11
+ export declare function isSkipped(pluginData: unknown): boolean;
12
+ export declare const skipData: {
13
+ "mint-skip": boolean;
14
+ };
15
+ export declare function readReactInfo(installRoot: string): {
16
+ info?: ReactInfo;
17
+ diagnostics: BuildDiagnostic[];
18
+ };
19
+ export declare function checkReactVersions(version: string, domVersion: string): BuildDiagnostic[];
20
+ export declare function packageOfPath(path: string): {
21
+ name: string;
22
+ dir: string;
23
+ } | undefined;
24
+ export type ReplacedReact = {
25
+ pkg: string;
26
+ dir: string;
27
+ version?: string;
28
+ };
29
+ export declare function singleReactPlugin(opts: {
30
+ installRoot: string;
31
+ onReplaced: (replaced: ReplacedReact) => void;
32
+ }): Plugin;
33
+ export declare const NAMESPACED: RegExp;
34
+ export declare function checkDuplicateReact(metafile: Metafile, absWorkingDir: string, label: string): BuildDiagnostic[];
35
+ export declare function reactPackageDirs(metafile: Metafile, absWorkingDir: string): Record<string, string[]>;
36
+ export declare function scanRemovedApis(metafile: Metafile, absWorkingDir: string, reactVersion: string): BuildDiagnostic[];
37
+ export declare function checkPeerRanges(metafile: Metafile, absWorkingDir: string, info: Pick<ReactInfo, 'version' | 'domVersion'>): BuildDiagnostic[];
package/dist/react.js ADDED
@@ -0,0 +1,264 @@
1
+ import { existsSync, readFileSync, realpathSync } from 'node:fs';
2
+ import { basename, join, resolve } from 'node:path';
3
+ import { majorOf, satisfies } from './semver.js';
4
+ export const SKIP_PLUGINS = 'mint-skip';
5
+ export const REACT_SPECIFIER = /^(react|react-dom)(\/.*)?$/;
6
+ export function isSkipped(pluginData) {
7
+ return (typeof pluginData === 'object' &&
8
+ pluginData !== null &&
9
+ pluginData[SKIP_PLUGINS] === true);
10
+ }
11
+ export const skipData = { [SKIP_PLUGINS]: true };
12
+ function readJson(path) {
13
+ try {
14
+ return JSON.parse(readFileSync(path, 'utf8'));
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ }
20
+ export function readReactInfo(installRoot) {
21
+ const diagnostics = [];
22
+ const dirs = {};
23
+ const versions = {};
24
+ for (const name of ['react', 'react-dom']) {
25
+ const dir = join(installRoot, 'node_modules', name);
26
+ const pkg = existsSync(dir) ? readJson(join(dir, 'package.json')) : undefined;
27
+ if (!pkg || typeof pkg.version !== 'string') {
28
+ diagnostics.push({
29
+ level: 'error',
30
+ code: 'react-missing',
31
+ message: `${name} is not installed. Add react and react-dom (18.x or 19.x) to package.json dependencies.`,
32
+ pkg: name,
33
+ });
34
+ continue;
35
+ }
36
+ dirs[name] = realpathSync(dir);
37
+ versions[name] = pkg.version;
38
+ }
39
+ const version = versions['react'];
40
+ const domVersion = versions['react-dom'];
41
+ const reactDir = dirs['react'];
42
+ const reactDomDir = dirs['react-dom'];
43
+ if (!version || !domVersion || !reactDir || !reactDomDir)
44
+ return { diagnostics };
45
+ return { info: { version, domVersion, reactDir, reactDomDir }, diagnostics };
46
+ }
47
+ export function checkReactVersions(version, domVersion) {
48
+ const out = [];
49
+ const major = majorOf(version);
50
+ const domMajor = majorOf(domVersion);
51
+ for (const [name, v, m] of [
52
+ ['react', version, major],
53
+ ['react-dom', domVersion, domMajor],
54
+ ]) {
55
+ if (m === undefined || m < 18) {
56
+ out.push({
57
+ level: 'error',
58
+ code: 'react-unsupported-version',
59
+ message: `${name} ${v} is not supported. Islands need React 18.x or 19.x.`,
60
+ pkg: name,
61
+ });
62
+ }
63
+ }
64
+ if (out.length > 0 || version === domVersion)
65
+ return out;
66
+ if (major !== domMajor || (major !== undefined && major >= 19)) {
67
+ out.push({
68
+ level: 'error',
69
+ code: 'react-version-mismatch',
70
+ message: `react ${version} and react-dom ${domVersion} must be the exact same version (React ${major}.x throws "Incompatible React versions" at render). Pin both to the same version.`,
71
+ pkg: 'react-dom',
72
+ });
73
+ }
74
+ else {
75
+ out.push({
76
+ level: 'warning',
77
+ code: 'react-version-mismatch',
78
+ message: `react ${version} and react-dom ${domVersion} differ. React 18 tolerates this, but pin both to the same version.`,
79
+ pkg: 'react-dom',
80
+ });
81
+ }
82
+ return out;
83
+ }
84
+ export function packageOfPath(path) {
85
+ const normalized = path.replace(/\\/g, '/');
86
+ const withSlash = normalized.startsWith('/') ? normalized : `/${normalized}`;
87
+ const idx = withSlash.lastIndexOf('/node_modules/');
88
+ if (idx === -1)
89
+ return undefined;
90
+ const rest = withSlash.slice(idx + '/node_modules/'.length);
91
+ const parts = rest.split('/');
92
+ const name = rest.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
93
+ if (!name)
94
+ return undefined;
95
+ const dir = withSlash.slice(0, idx + '/node_modules/'.length) + name;
96
+ return { name, dir: normalized.startsWith('/') ? dir : dir.slice(1) };
97
+ }
98
+ export function singleReactPlugin(opts) {
99
+ return {
100
+ name: 'mint-single-react',
101
+ setup(build) {
102
+ build.onResolve({ filter: REACT_SPECIFIER }, async (args) => {
103
+ if (isSkipped(args.pluginData))
104
+ return undefined;
105
+ const aliased = await build.resolve(args.path, {
106
+ kind: args.kind,
107
+ resolveDir: opts.installRoot,
108
+ pluginData: skipData,
109
+ });
110
+ if (aliased.errors.length > 0) {
111
+ return {
112
+ errors: [
113
+ {
114
+ text: `Could not resolve "${args.path}" from the docs project's node_modules. Add react and react-dom to package.json dependencies.`,
115
+ },
116
+ ],
117
+ };
118
+ }
119
+ if (/[\\/]node_modules[\\/]/.test(args.importer)) {
120
+ const natural = await build.resolve(args.path, {
121
+ kind: args.kind,
122
+ resolveDir: args.resolveDir,
123
+ pluginData: skipData,
124
+ });
125
+ const naturalPkg = natural.errors.length === 0 ? packageOfPath(natural.path) : undefined;
126
+ const aliasedPkg = packageOfPath(aliased.path);
127
+ const importerPkg = packageOfPath(args.importer);
128
+ if (naturalPkg && aliasedPkg && importerPkg && naturalPkg.dir !== aliasedPkg.dir) {
129
+ const nested = readJson(join(naturalPkg.dir, 'package.json'));
130
+ opts.onReplaced({
131
+ pkg: importerPkg.name,
132
+ dir: naturalPkg.dir,
133
+ version: typeof nested?.version === 'string' ? nested.version : undefined,
134
+ });
135
+ }
136
+ }
137
+ return { path: aliased.path, sideEffects: aliased.sideEffects };
138
+ });
139
+ },
140
+ };
141
+ }
142
+ export const NAMESPACED = /^(?![A-Za-z]:[\\/])[^\\/]+:/;
143
+ function inputPackages(metafile, absWorkingDir) {
144
+ const byDir = new Map();
145
+ for (const input of Object.keys(metafile.inputs)) {
146
+ if (NAMESPACED.test(input))
147
+ continue;
148
+ const pkg = packageOfPath(input);
149
+ if (!pkg)
150
+ continue;
151
+ const dir = resolve(absWorkingDir, pkg.dir);
152
+ const declared = readJson(join(dir, 'package.json'))?.name;
153
+ const name = typeof declared === 'string' ? declared : pkg.name;
154
+ const entry = byDir.get(dir) ?? { name, dir, files: [] };
155
+ entry.files.push(resolve(absWorkingDir, input));
156
+ byDir.set(dir, entry);
157
+ }
158
+ return [...byDir.values()];
159
+ }
160
+ const REACT_BUILD = /^(react|react-dom(?:-[a-z]+)*)\.(production|development|profiling)(\.min)?\.js$/;
161
+ function vendoredBuild(file, name) {
162
+ const built = REACT_BUILD.exec(basename(file))?.[1];
163
+ if (built === undefined)
164
+ return false;
165
+ return name === 'react' ? built === 'react' : built.startsWith('react-dom');
166
+ }
167
+ export function checkDuplicateReact(metafile, absWorkingDir, label) {
168
+ const out = [];
169
+ const pkgs = inputPackages(metafile, absWorkingDir);
170
+ for (const name of ['react', 'react-dom']) {
171
+ const reactDirs = pkgs.filter((p) => p.name === name).map((p) => p.dir);
172
+ const vendored = pkgs
173
+ .filter((p) => p.name !== 'react' && p.name !== 'react-dom')
174
+ .filter((p) => p.files.some((file) => vendoredBuild(file, name)))
175
+ .map((p) => `${p.dir} (bundled into ${p.name})`);
176
+ const dirs = [...reactDirs, ...vendored];
177
+ if (dirs.length > 1) {
178
+ out.push({
179
+ level: 'error',
180
+ code: 'react-duplicate',
181
+ message: `The ${label} bundle contains ${dirs.length} copies of ${name}: ${dirs.join(', ')}. Islands need exactly one React.`,
182
+ pkg: name,
183
+ });
184
+ }
185
+ }
186
+ return out;
187
+ }
188
+ export function reactPackageDirs(metafile, absWorkingDir) {
189
+ const pkgs = inputPackages(metafile, absWorkingDir);
190
+ return {
191
+ react: pkgs.filter((p) => p.name === 'react').map((p) => p.dir),
192
+ 'react-dom': pkgs.filter((p) => p.name === 'react-dom').map((p) => p.dir),
193
+ };
194
+ }
195
+ const REACT_OWN_PACKAGES = new Set(['react', 'react-dom', 'scheduler', 'react-is']);
196
+ const REMOVED_APIS = [
197
+ { pattern: /\bfindDOMNode\b/, api: 'findDOMNode' },
198
+ {
199
+ pattern: /\b(?:ReactDOM|ReactDom|_reactDom)\.(?:render|hydrate|unmountComponentAtNode)\s*\(/,
200
+ api: 'ReactDOM.render',
201
+ },
202
+ { pattern: /\.defaultProps\s*=(?!=)/, api: '.defaultProps =' },
203
+ ];
204
+ export function scanRemovedApis(metafile, absWorkingDir, reactVersion) {
205
+ const major = majorOf(reactVersion);
206
+ if (major === undefined || major < 19)
207
+ return [];
208
+ const out = [];
209
+ for (const pkg of inputPackages(metafile, absWorkingDir)) {
210
+ if (REACT_OWN_PACKAGES.has(pkg.name))
211
+ continue;
212
+ const found = new Set();
213
+ for (const file of pkg.files) {
214
+ let src;
215
+ try {
216
+ src = readFileSync(file, 'utf8');
217
+ }
218
+ catch {
219
+ continue;
220
+ }
221
+ for (const { pattern, api } of REMOVED_APIS) {
222
+ if (!found.has(api) && pattern.test(src))
223
+ found.add(api);
224
+ }
225
+ if (found.size === REMOVED_APIS.length)
226
+ break;
227
+ }
228
+ if (found.size > 0) {
229
+ out.push({
230
+ level: 'warning',
231
+ code: 'react-removed-api',
232
+ message: `${pkg.name} uses APIs that React ${major} removed (${[...found].join(', ')}). It may crash or render wrong output; pin React 18 if it does.`,
233
+ pkg: pkg.name,
234
+ });
235
+ }
236
+ }
237
+ return out;
238
+ }
239
+ export function checkPeerRanges(metafile, absWorkingDir, info) {
240
+ const out = [];
241
+ const installed = { react: info.version, 'react-dom': info.domVersion };
242
+ for (const pkg of inputPackages(metafile, absWorkingDir)) {
243
+ if (REACT_OWN_PACKAGES.has(pkg.name))
244
+ continue;
245
+ const json = readJson(join(pkg.dir, 'package.json'));
246
+ const peers = json?.peerDependencies;
247
+ if (typeof peers !== 'object' || peers === null)
248
+ continue;
249
+ for (const [peer, version] of Object.entries(installed)) {
250
+ const range = peers[peer];
251
+ if (typeof range !== 'string')
252
+ continue;
253
+ if (satisfies(version, range) === false) {
254
+ out.push({
255
+ level: 'warning',
256
+ code: 'react-peer-range',
257
+ message: `${pkg.name}@${String(json?.version ?? '?')} declares peer ${peer}@"${range}", which excludes the installed ${peer} ${version}.`,
258
+ pkg: pkg.name,
259
+ });
260
+ }
261
+ }
262
+ }
263
+ return out;
264
+ }
@@ -0,0 +1,37 @@
1
+ export declare const ISLAND_RENDER_TIMEOUT_MS = 3000;
2
+ export type RenderRequest = {
3
+ id: string;
4
+ prefix: string;
5
+ };
6
+ export declare function permissionArgs(readable: string[], flags?: ReadonlySet<string>): string[];
7
+ export declare function disabledModuleArgs(flags?: ReadonlySet<string>): string[];
8
+ export declare function sandboxAvailable(flags?: ReadonlySet<string>): boolean;
9
+ export declare const ISLAND_RENDER_MEMORY_LIMIT_BYTES: number;
10
+ export declare function sandboxArgs(args: string[], probe: () => Promise<boolean>, platform?: NodeJS.Platform): Promise<string[]>;
11
+ export type RenderOutcome = {
12
+ id: string;
13
+ ok: true;
14
+ html: string;
15
+ second?: string;
16
+ ssrMs: number;
17
+ importMs: number;
18
+ errors: string[];
19
+ warnings: string[];
20
+ } | {
21
+ id: string;
22
+ ok: false;
23
+ error: string;
24
+ errors: string[];
25
+ warnings: string[];
26
+ };
27
+ type RenderOptions = {
28
+ serverDir: string;
29
+ islands: RenderRequest[];
30
+ timeoutMs?: number;
31
+ memoryLimitBytes?: number;
32
+ requireSandbox?: boolean;
33
+ platform?: NodeJS.Platform;
34
+ };
35
+ export declare function renderEach(opts: RenderOptions): AsyncGenerator<RenderOutcome>;
36
+ export declare function renderIslands(opts: RenderOptions): Promise<RenderOutcome[]>;
37
+ export {};