@fairgarden/monolith 0.1.0-alpha.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 (50) hide show
  1. package/Readme.md +84 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +160 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/compat.d.ts +4 -0
  7. package/dist/compat.d.ts.map +1 -0
  8. package/dist/compat.js +156 -0
  9. package/dist/compat.js.map +1 -0
  10. package/dist/eslint.d.ts +62 -0
  11. package/dist/eslint.d.ts.map +1 -0
  12. package/dist/eslint.js +198 -0
  13. package/dist/eslint.js.map +1 -0
  14. package/dist/index.d.ts +28 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +77 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/link.d.ts +24 -0
  19. package/dist/link.d.ts.map +1 -0
  20. package/dist/link.js +473 -0
  21. package/dist/link.js.map +1 -0
  22. package/dist/mounts.d.ts +37 -0
  23. package/dist/mounts.d.ts.map +1 -0
  24. package/dist/mounts.js +51 -0
  25. package/dist/mounts.js.map +1 -0
  26. package/dist/package-json.d.ts +38 -0
  27. package/dist/package-json.d.ts.map +1 -0
  28. package/dist/package-json.js +156 -0
  29. package/dist/package-json.js.map +1 -0
  30. package/dist/portability.d.ts +36 -0
  31. package/dist/portability.d.ts.map +1 -0
  32. package/dist/portability.js +158 -0
  33. package/dist/portability.js.map +1 -0
  34. package/dist/portable-link.d.ts +22 -0
  35. package/dist/portable-link.d.ts.map +1 -0
  36. package/dist/portable-link.js +24 -0
  37. package/dist/portable-link.js.map +1 -0
  38. package/dist/resolve.d.ts +5 -0
  39. package/dist/resolve.d.ts.map +1 -0
  40. package/dist/resolve.js +181 -0
  41. package/dist/resolve.js.map +1 -0
  42. package/dist/routes.d.ts +23 -0
  43. package/dist/routes.d.ts.map +1 -0
  44. package/dist/routes.js +95 -0
  45. package/dist/routes.js.map +1 -0
  46. package/dist/types.d.ts +111 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +2 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +74 -0
@@ -0,0 +1,156 @@
1
+ import { readFile, readdir, writeFile } from 'node:fs/promises';
2
+ import { isBuiltin } from 'node:module';
3
+ import path from 'node:path';
4
+ import mergePackageJson from 'merge-package.json';
5
+ const DEPENDENCY_KEYS = [
6
+ 'dependencies',
7
+ 'devDependencies',
8
+ 'optionalDependencies',
9
+ 'peerDependencies',
10
+ ];
11
+ const SOURCE_EXTENSIONS = new Set([
12
+ '.ts',
13
+ '.tsx',
14
+ '.js',
15
+ '.jsx',
16
+ '.mjs',
17
+ '.cjs',
18
+ ]);
19
+ /** `import x from 'y'`, `import 'y'`, `import('y')` and `require('y')`. */
20
+ const SPECIFIER = /(?:from|import|require)\s*\(?\s*['"]([^'"]+)['"]/g;
21
+ const read = async (file) => JSON.parse(await readFile(file, 'utf8'));
22
+ /** `@scope/name/sub` -> `@scope/name`, `name/sub` -> `name`. */
23
+ const packageOf = (specifier) => {
24
+ const segments = specifier.split('/');
25
+ return specifier.startsWith('@') ? segments.slice(0, 2).join('/') : segments[0];
26
+ };
27
+ const sourceFiles = async (dir) => {
28
+ const found = [];
29
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
30
+ for (const entry of entries) {
31
+ if (entry.name === 'node_modules')
32
+ continue;
33
+ const full = path.join(dir, entry.name);
34
+ if (entry.isDirectory()) {
35
+ found.push(...(await sourceFiles(full)));
36
+ }
37
+ else if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
38
+ found.push(full);
39
+ }
40
+ }
41
+ return found;
42
+ };
43
+ /**
44
+ * Packages the app's route files import directly.
45
+ *
46
+ * Both route trees need these from the monolith, for different reasons. Pages
47
+ * Router files are copied, so their real path is inside the monolith and the
48
+ * app's `node_modules` is no longer above them. App Router files are only
49
+ * symlinked, and Turbopack does resolve those against the app — but TypeScript
50
+ * resolves from where the file sits in the monolith, so it needs them too.
51
+ *
52
+ * Only *direct* imports matter. Anything an app reaches through its own package
53
+ * name resolves via the monolith's link to the app and carries on from there.
54
+ */
55
+ export const routeImports = async (app) => {
56
+ const imported = new Set();
57
+ const roots = [app.appDir, app.pagesDir].filter((root) => root !== undefined);
58
+ for (const file of (await Promise.all(roots.map(sourceFiles))).flat()) {
59
+ const contents = await readFile(file, 'utf8').catch(() => '');
60
+ for (const [, specifier] of contents.matchAll(SPECIFIER)) {
61
+ if (specifier.startsWith('.') || isBuiltin(specifier))
62
+ continue;
63
+ const name = packageOf(specifier);
64
+ // Resolves through the monolith's link to the app, not its own deps.
65
+ if (name === app.packageName)
66
+ continue;
67
+ imported.add(name);
68
+ }
69
+ }
70
+ return imported;
71
+ };
72
+ /** `@scope/name` -> `@types/scope__name`, matching DefinitelyTyped's layout. */
73
+ const typesPackageOf = (name) => name.startsWith('@')
74
+ ? `@types/${name.slice(1).replace('/', '__')}`
75
+ : `@types/${name}`;
76
+ /**
77
+ * Workspace siblings are linked, not installed, so carrying their version
78
+ * ranges into the monolith would pin it against its own workspace.
79
+ */
80
+ const isInstallable = (range) => typeof range === 'string' &&
81
+ !range.startsWith('workspace:') &&
82
+ !range.startsWith('link:');
83
+ const pick = (pkg, wanted) => {
84
+ const picked = {};
85
+ for (const key of DEPENDENCY_KEYS) {
86
+ const deps = pkg[key];
87
+ if (!deps)
88
+ continue;
89
+ const taken = Object.entries(deps).filter(([name, range]) => wanted.has(name) && isInstallable(range));
90
+ if (taken.length > 0)
91
+ picked[key] = Object.fromEntries(taken);
92
+ }
93
+ return picked;
94
+ };
95
+ const everything = (pkg) => {
96
+ const picked = {};
97
+ for (const key of DEPENDENCY_KEYS) {
98
+ const deps = pkg[key];
99
+ if (!deps)
100
+ continue;
101
+ const taken = Object.entries(deps).filter(([, range]) => isInstallable(range));
102
+ if (taken.length > 0)
103
+ picked[key] = Object.fromEntries(taken);
104
+ }
105
+ return picked;
106
+ };
107
+ /**
108
+ * Fold what each app needs from the monolith into its package.json.
109
+ *
110
+ * Uses a three-way merge against an empty base so conflicting ranges are
111
+ * resolved by semver rather than by whichever app happens to be last.
112
+ */
113
+ export const mergeAppDependencies = async (monolithPackageJsonPath, apps, options = {}) => {
114
+ const original = await read(monolithPackageJsonPath);
115
+ let merged = JSON.stringify(original);
116
+ const taken = new Map();
117
+ for (const app of apps) {
118
+ const appPkg = await read(path.join(app.root, 'package.json'));
119
+ let incoming;
120
+ if (options.all) {
121
+ incoming = everything(appPkg);
122
+ }
123
+ else {
124
+ const wanted = await routeImports(app);
125
+ // Type declarations are resolved from the monolith too.
126
+ for (const name of [...wanted])
127
+ wanted.add(typesPackageOf(name));
128
+ incoming = pick(appPkg, wanted);
129
+ }
130
+ const names = DEPENDENCY_KEYS.flatMap((key) => Object.keys(incoming[key] ?? {}));
131
+ if (names.length === 0)
132
+ continue;
133
+ taken.set(app.name, names);
134
+ merged = mergePackageJson(merged, JSON.stringify({}), JSON.stringify(incoming));
135
+ }
136
+ const packageJson = JSON.parse(merged);
137
+ return {
138
+ packageJson,
139
+ // Compared by content, not by key order: the merge re-sorts, and a
140
+ // monolith whose dependencies sit in any other order would otherwise fail
141
+ // `--check` on every run with nothing to actually change.
142
+ changed: !sameDependencies(original, packageJson),
143
+ taken,
144
+ };
145
+ };
146
+ /** Whether two manifests declare the same dependencies, order aside. */
147
+ const sameDependencies = (a, b) => DEPENDENCY_KEYS.every((key) => {
148
+ const left = (a[key] ?? {});
149
+ const right = (b[key] ?? {});
150
+ const names = new Set([...Object.keys(left), ...Object.keys(right)]);
151
+ return [...names].every((name) => left[name] === right[name]);
152
+ });
153
+ export const writePackageJson = async (file, packageJson) => {
154
+ await writeFile(file, `${JSON.stringify(packageJson, null, 2)}\n`);
155
+ };
156
+ //# sourceMappingURL=package-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-json.js","sourceRoot":"","sources":["../src/package-json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { NextConfig } from 'next';
2
+ export interface PortabilityOptions {
3
+ /**
4
+ * The app to check. Defaults to the directory of the config that called
5
+ * this, not the working directory — a monolith loading an app's config
6
+ * would otherwise have the app checked against the monolith's own files.
7
+ */
8
+ root?: string;
9
+ /** `warn` reports and continues, `error` refuses to build. Defaults to `warn`. */
10
+ level?: 'warn' | 'error';
11
+ /** Findings to accept, by the text before the colon. */
12
+ ignore?: string[];
13
+ }
14
+ /**
15
+ * Everything about this app that would not survive being mounted in a monolith.
16
+ *
17
+ * Exported separately so a test or a CI check can assert on the list rather
18
+ * than scrape log output.
19
+ */
20
+ export declare const findPortabilityProblems: (nextConfig: NextConfig, root: string) => string[];
21
+ /**
22
+ * Check that an app can be mounted into a monolith, from the app's own config.
23
+ *
24
+ * Wrap the app's config with this and it reports, on every build, whatever
25
+ * would not survive being served under a prefix alongside other apps. It
26
+ * changes nothing about how the app runs on its own.
27
+ *
28
+ * ```ts
29
+ * // apps/id/next.config.ts
30
+ * export default withMonolithicPortability({ redirects: async () => [...] })
31
+ * ```
32
+ *
33
+ * Set `level: 'error'` in CI to make it a gate rather than a reminder.
34
+ */
35
+ export declare const withMonolithicPortability: (nextConfig?: NextConfig, options?: PortabilityOptions) => NextConfig;
36
+ //# sourceMappingURL=portability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portability.d.ts","sourceRoot":"","sources":["../src/portability.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAgEtC,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACxB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,YAAY,UAAU,EACtB,MAAM,MAAM,KACX,MAAM,EAoCR,CAAA;AAiDD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,GACpC,aAAY,UAAe,EAC3B,UAAS,kBAAuB,KAC/B,UAiBF,CAAA"}
@@ -0,0 +1,158 @@
1
+ import { statSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Options the monolith owns for the whole deployment. An app that sets one is
5
+ * either fighting the monolith or silently changing every other app.
6
+ */
7
+ const OWNED_OPTIONS = ['basePath', 'distDir'];
8
+ /** Options that have to agree across every app, because one server serves all. */
9
+ const SHARED_OPTIONS = [
10
+ 'i18n',
11
+ 'trailingSlash',
12
+ 'skipTrailingSlashRedirect',
13
+ 'skipMiddlewareUrlNormalize',
14
+ 'output',
15
+ 'assetPrefix',
16
+ ];
17
+ /**
18
+ * Conventions Next allows exactly one of per deployment. An app can use them on
19
+ * its own, but mounting several apps means only one could ever win — so rather
20
+ * than let one app's copy silently disappear, say so.
21
+ */
22
+ const SINGLETON_FILES = [
23
+ {
24
+ names: ['proxy', 'middleware', 'src/proxy', 'src/middleware'],
25
+ why: 'a deployment has one proxy; the monolith would have to run it and strip the mount prefix itself',
26
+ },
27
+ {
28
+ names: ['instrumentation', 'src/instrumentation'],
29
+ why: 'a deployment has one instrumentation hook',
30
+ },
31
+ {
32
+ names: ['pages/_app', 'src/pages/_app'],
33
+ why: 'a deployment has one Pages Router app shell',
34
+ },
35
+ {
36
+ names: ['pages/_document', 'src/pages/_document'],
37
+ why: 'a deployment has one Pages Router document',
38
+ },
39
+ ];
40
+ /**
41
+ * Files that generate a route which only means anything at the site root.
42
+ * Mounted, they end up at `/<name>/robots.txt`, where nothing looks for them.
43
+ */
44
+ const ROOT_ONLY_ROUTES = ['robots', 'sitemap', 'manifest'];
45
+ const EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs'];
46
+ const exists = (candidate) => {
47
+ try {
48
+ statSync(candidate);
49
+ return true;
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ };
55
+ const findFile = (root, name) => EXTENSIONS.map((extension) => `${name}${extension}`).find((file) => exists(path.join(root, file)));
56
+ /**
57
+ * Everything about this app that would not survive being mounted in a monolith.
58
+ *
59
+ * Exported separately so a test or a CI check can assert on the list rather
60
+ * than scrape log output.
61
+ */
62
+ export const findPortabilityProblems = (nextConfig, root) => {
63
+ const problems = [];
64
+ for (const option of OWNED_OPTIONS) {
65
+ if (nextConfig[option] !== undefined) {
66
+ problems.push(`${option}: only the monolith may set this; mounted apps are served from their mount point instead`);
67
+ }
68
+ }
69
+ for (const option of SHARED_OPTIONS) {
70
+ if (nextConfig[option] !== undefined) {
71
+ problems.push(`${option}: has to match every other app in the monolith, so it belongs on the monolith`);
72
+ }
73
+ }
74
+ for (const { names, why } of SINGLETON_FILES) {
75
+ const found = names.map((name) => findFile(root, name)).find(Boolean);
76
+ if (found)
77
+ problems.push(`${found}: ${why}`);
78
+ }
79
+ for (const name of ROOT_ONLY_ROUTES) {
80
+ const found = findFile(root, path.join('app', name)) ??
81
+ findFile(root, path.join('src', 'app', name));
82
+ if (found) {
83
+ problems.push(`${found}: serves a route that only works at the site root, and would be mounted under the app's prefix`);
84
+ }
85
+ }
86
+ return problems;
87
+ };
88
+ /**
89
+ * Next reads the config in the main process and again in a build worker.
90
+ * Marking the environment keeps the report to one, since the worker inherits it.
91
+ */
92
+ /**
93
+ * The directory of the config that called this.
94
+ *
95
+ * A monolith loads each app's own config, so the working directory is the
96
+ * monolith's and would attribute its files to whichever app is being loaded.
97
+ */
98
+ const callerDirectory = () => {
99
+ const original = Error.prepareStackTrace;
100
+ try {
101
+ Error.prepareStackTrace = (_, stack) => stack;
102
+ const stack = new Error().stack;
103
+ for (const frame of stack ?? []) {
104
+ const file = frame.getFileName?.();
105
+ if (!file || file.includes('node_modules') || !file.includes('next.config')) {
106
+ continue;
107
+ }
108
+ return path.dirname(file.replace(/^file:\/\//, ''));
109
+ }
110
+ }
111
+ catch {
112
+ return undefined;
113
+ }
114
+ finally {
115
+ Error.prepareStackTrace = original;
116
+ }
117
+ return undefined;
118
+ };
119
+ const REPORTED_ENV = '__FG_MONOLITH_PORTABILITY_REPORTED';
120
+ const alreadyReported = (root) => (process.env[REPORTED_ENV] ?? '').split(path.delimiter).includes(root);
121
+ const markReported = (root) => {
122
+ const seen = (process.env[REPORTED_ENV] ?? '').split(path.delimiter).filter(Boolean);
123
+ process.env[REPORTED_ENV] = [...seen, root].join(path.delimiter);
124
+ };
125
+ const describe = (problems) => [
126
+ `This app would not port cleanly into a monolith:`,
127
+ ...problems.map((problem) => ` - ${problem}`),
128
+ `See @fairgarden/monolith for what a mounted app can rely on.`,
129
+ ].join('\n');
130
+ /**
131
+ * Check that an app can be mounted into a monolith, from the app's own config.
132
+ *
133
+ * Wrap the app's config with this and it reports, on every build, whatever
134
+ * would not survive being served under a prefix alongside other apps. It
135
+ * changes nothing about how the app runs on its own.
136
+ *
137
+ * ```ts
138
+ * // apps/id/next.config.ts
139
+ * export default withMonolithicPortability({ redirects: async () => [...] })
140
+ * ```
141
+ *
142
+ * Set `level: 'error'` in CI to make it a gate rather than a reminder.
143
+ */
144
+ export const withMonolithicPortability = (nextConfig = {}, options = {}) => {
145
+ const root = options.root ?? callerDirectory() ?? process.cwd();
146
+ const ignore = new Set(options.ignore ?? []);
147
+ const problems = findPortabilityProblems(nextConfig, root).filter((problem) => !ignore.has(problem.slice(0, problem.indexOf(':'))));
148
+ if (problems.length > 0) {
149
+ if (options.level === 'error')
150
+ throw new Error(describe(problems));
151
+ if (!alreadyReported(root)) {
152
+ markReported(root);
153
+ process.stderr.write(`${describe(problems)}\n`);
154
+ }
155
+ }
156
+ return nextConfig;
157
+ };
158
+ //# sourceMappingURL=portability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portability.js","sourceRoot":"","sources":["../src/portability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import NextLink from 'next/link';
2
+ import type { ComponentProps } from 'react';
3
+ export { createHref, mountPrefix, prefixHref, MOUNTS_ENV } from './mounts.ts';
4
+ type NextLinkProps = ComponentProps<typeof NextLink>;
5
+ /**
6
+ * A `next/link` that knows where its app is mounted.
7
+ *
8
+ * The monolith does not use `basePath`, so nothing prefixes an app's own hrefs
9
+ * for it. Build one of these per app and use it in place of `next/link`:
10
+ *
11
+ * ```tsx
12
+ * // lib/link.ts
13
+ * export const Link = createLink('@fairgarden/id')
14
+ * ```
15
+ *
16
+ * Standalone the prefix is empty and this is `next/link` with an extra call.
17
+ */
18
+ export declare const createLink: (packageName: string) => {
19
+ ({ href, ...props }: NextLinkProps): import("react").JSX.Element;
20
+ displayName: string;
21
+ };
22
+ //# sourceMappingURL=portable-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portable-link.d.ts","sourceRoot":"","sources":["../src/portable-link.tsx"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,WAAW,CAAA;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAG3C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7E,KAAK,aAAa,GAAG,cAAc,CAAC,OAAO,QAAQ,CAAC,CAAA;AAEpD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM;yBAGF,aAAa;;CAKxD,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import NextLink from 'next/link';
3
+ import { mountPrefix, prefixHref } from "./mounts.js";
4
+ export { createHref, mountPrefix, prefixHref, MOUNTS_ENV } from "./mounts.js";
5
+ /**
6
+ * A `next/link` that knows where its app is mounted.
7
+ *
8
+ * The monolith does not use `basePath`, so nothing prefixes an app's own hrefs
9
+ * for it. Build one of these per app and use it in place of `next/link`:
10
+ *
11
+ * ```tsx
12
+ * // lib/link.ts
13
+ * export const Link = createLink('@fairgarden/id')
14
+ * ```
15
+ *
16
+ * Standalone the prefix is empty and this is `next/link` with an extra call.
17
+ */
18
+ export const createLink = (packageName) => {
19
+ const prefix = mountPrefix(packageName);
20
+ const MonolithLink = ({ href, ...props }) => (_jsx(NextLink, { href: prefixHref(href, prefix), ...props }));
21
+ MonolithLink.displayName = `MonolithLink(${packageName})`;
22
+ return MonolithLink;
23
+ };
24
+ //# sourceMappingURL=portable-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portable-link.js","sourceRoot":"","sources":["../src/portable-link.tsx"],"names":[],"mappings":";AAAA,OAAO,QAAQ,MAAM,WAAW,CAAA;AAEhC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAI7E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,WAAmB,EAAE,EAAE;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,WAAW,CAAC,CAAA;IAEvC,MAAM,YAAY,GAAG,CAAC,EAAE,IAAI,EAAE,GAAG,KAAK,EAAiB,EAAE,EAAE,CAAC,CAC1D,KAAC,QAAQ,IAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,KAAM,KAAK,GAAI,CACxD,CAAA;IACD,YAAY,CAAC,WAAW,GAAG,gBAAgB,WAAW,GAAG,CAAA;IACzD,OAAO,YAAY,CAAA;AACrB,CAAC,CAAA"}
@@ -0,0 +1,5 @@
1
+ import type { MonolithApps, ResolvedApp } from './types.ts';
2
+ /** Where an app's files were taken from, when more than one source was offered. */
3
+ export type AppSource = 'path' | 'package';
4
+ export declare const resolveApps: (monolithRoot: string, apps: MonolithApps, phase: string) => Promise<ResolvedApp[]>;
5
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAe,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAqCxE,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,CAAA;AA+H1C,eAAO,MAAM,WAAW,GACtB,cAAc,MAAM,EACpB,MAAM,YAAY,EAClB,OAAO,MAAM,KACZ,OAAO,CAAC,WAAW,EAAE,CAqDrB,CAAA"}
@@ -0,0 +1,181 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { createRequire } from 'node:module';
10
+ import { readFileSync, realpathSync, statSync } from 'node:fs';
11
+ import path from 'node:path';
12
+ import { pathToFileURL } from 'node:url';
13
+ /** Config file names Next itself accepts, in the order Next looks for them. */
14
+ const CONFIG_FILES = [
15
+ 'next.config.ts',
16
+ 'next.config.mts',
17
+ 'next.config.js',
18
+ 'next.config.mjs',
19
+ 'next.config.cjs',
20
+ ];
21
+ const isDirectory = (candidate) => {
22
+ try {
23
+ return statSync(candidate).isDirectory();
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ };
29
+ const isFile = (candidate) => {
30
+ try {
31
+ return statSync(candidate).isFile();
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ };
37
+ /** Mount names become URL segments, so keep them to something safe. */
38
+ const assertMountName = (name) => {
39
+ if (!/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {
40
+ throw new Error(`Monolith app name ${JSON.stringify(name)} is not usable as a URL segment. ` +
41
+ 'Use letters, digits, dashes and underscores.');
42
+ }
43
+ };
44
+ const resolvePackage = (monolithRoot, pkg) => {
45
+ const require = createRequire(path.join(monolithRoot, 'package.json'));
46
+ let resolved;
47
+ try {
48
+ resolved = path.dirname(require.resolve(`${pkg}/package.json`));
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ // A workspace package resolves through node_modules but is a link to the
54
+ // checkout, so it is the same thing as naming its path: it belongs in the
55
+ // committed tree and can be symlinked, not copied.
56
+ const real = realpathSync.native(resolved);
57
+ const installed = real.split(path.sep).includes('node_modules');
58
+ return { source: installed ? 'package' : 'path', root: real };
59
+ };
60
+ /**
61
+ * An app checked out at a path is usable only if its routes are actually there.
62
+ *
63
+ * A submodule that was never cloned leaves an empty directory behind, which is
64
+ * what a build host produces when it cannot read the submodule's remote.
65
+ */
66
+ const hasRoutes = (root, app) => {
67
+ if (app.appDir)
68
+ return isDirectory(path.resolve(root, app.appDir));
69
+ // Only an App Router tree counts: resolveApps requires one, so accepting a
70
+ // pages-only checkout here would pick it and then fail instead of trying the
71
+ // package that was offered as a fallback.
72
+ return [path.join(root, 'app'), path.join(root, 'src', 'app')].some(isDirectory);
73
+ };
74
+ /**
75
+ * Locate an app on disk.
76
+ *
77
+ * Paths are taken relative to the monolith and packages are resolved from
78
+ * node_modules. Both may be given, and then the checkout wins when it is
79
+ * there and the package is the fallback — which is how a private module can
80
+ * still build somewhere that could not clone the submodule but can install
81
+ * the package.
82
+ */
83
+ const resolveRoot = (monolithRoot, name, app) => {
84
+ const candidates = [];
85
+ if (app.root) {
86
+ candidates.push({ source: 'path', root: path.resolve(monolithRoot, app.root) });
87
+ }
88
+ if (app.package) {
89
+ const resolved = resolvePackage(monolithRoot, app.package);
90
+ if (resolved)
91
+ candidates.push(resolved);
92
+ }
93
+ if (candidates.length === 0) {
94
+ if (app.package) {
95
+ throw new Error(`Could not resolve monolith app ${JSON.stringify(name)} from package ` +
96
+ `${JSON.stringify(app.package)}. Is it a dependency of the monolith?`);
97
+ }
98
+ throw new Error(`Monolith app ${JSON.stringify(name)} needs either "root" or "package".`);
99
+ }
100
+ const usable = candidates.find((candidate) => isDirectory(candidate.root) && hasRoutes(candidate.root, app));
101
+ if (usable)
102
+ return usable;
103
+ // Nothing usable: say which places were looked at rather than just the last.
104
+ const looked = candidates
105
+ .map((candidate) => `${candidate.source} ${candidate.root}`)
106
+ .join(', ');
107
+ throw new Error(`Monolith app ${JSON.stringify(name)} has no routes directory. Looked at ${looked}. ` +
108
+ 'An empty directory here is usually a submodule that was never cloned.');
109
+ };
110
+ /** A bare string is a path when it looks like one, otherwise a package name. */
111
+ const normalize = (entry) => {
112
+ if (typeof entry !== 'string')
113
+ return entry;
114
+ return entry.startsWith('.') || path.isAbsolute(entry)
115
+ ? { root: entry }
116
+ : { package: entry };
117
+ };
118
+ const readPackageName = (root) => {
119
+ try {
120
+ const pkg = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8'));
121
+ return typeof pkg.name === 'string' ? pkg.name : undefined;
122
+ }
123
+ catch {
124
+ return undefined;
125
+ }
126
+ };
127
+ const loadNextConfig = async (root, phase, name) => {
128
+ const configPath = CONFIG_FILES.map((file) => path.join(root, file)).find(isFile);
129
+ if (!configPath)
130
+ return {};
131
+ let loaded;
132
+ try {
133
+ loaded = (await import(__rewriteRelativeImportExtension(pathToFileURL(configPath).href))).default;
134
+ }
135
+ catch (cause) {
136
+ throw new Error(`Failed to load the Next config for monolith app ${JSON.stringify(name)} ` +
137
+ `at ${configPath}.`, { cause });
138
+ }
139
+ const config = typeof loaded === 'function' ? await loaded(phase) : loaded;
140
+ return (config ?? {});
141
+ };
142
+ export const resolveApps = async (monolithRoot, apps, phase) => Promise.all(Object.entries(apps).map(async ([name, entry]) => {
143
+ assertMountName(name);
144
+ const app = normalize(entry);
145
+ const { root, source } = resolveRoot(monolithRoot, name, app);
146
+ const appDir = app.appDir
147
+ ? path.resolve(root, app.appDir)
148
+ : [path.join(root, 'app'), path.join(root, 'src', 'app')].find(isDirectory);
149
+ if (!appDir || !isDirectory(appDir)) {
150
+ throw new Error(`Monolith app ${JSON.stringify(name)} has no routes directory. ` +
151
+ `Looked for "app/" and "src/app/" in ${root}.`);
152
+ }
153
+ const publicDirCandidate = app.publicDir
154
+ ? path.resolve(root, app.publicDir)
155
+ : path.join(root, 'public');
156
+ const pagesDir = app.pagesDir
157
+ ? path.resolve(root, app.pagesDir)
158
+ : [path.join(root, 'pages'), path.join(root, 'src', 'pages')].find(isDirectory);
159
+ const pagesApiDir = pagesDir && isDirectory(path.join(pagesDir, 'api'))
160
+ ? path.join(pagesDir, 'api')
161
+ : undefined;
162
+ const nextConfig = app.nextConfig === false
163
+ ? {}
164
+ : (app.nextConfig ??
165
+ (await loadNextConfig(root, phase, name)));
166
+ return {
167
+ name,
168
+ // '' rather than '/', so prefixing a path is a plain concatenation.
169
+ prefix: app.prefix === '/' || app.prefix === '' ? '' : (app.prefix ?? `/${name}`),
170
+ root,
171
+ packageName: readPackageName(root),
172
+ source,
173
+ appDir,
174
+ publicDir: isDirectory(publicDirCandidate) ? publicDirCandidate : undefined,
175
+ pagesDir: pagesDir && isDirectory(pagesDir) ? pagesDir : undefined,
176
+ pagesApiDir,
177
+ apiViaPages: pagesApiDir !== undefined,
178
+ nextConfig: typeof nextConfig === 'function' ? await nextConfig(phase) : nextConfig,
179
+ };
180
+ }));
181
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}