@dbx-tools/projen 0.4.1 → 0.5.1
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/index.ts +92 -52
- package/package.json +1 -1
- package/src/barrels.ts +18 -18
- package/src/clean.ts +2 -2
- package/src/codegen.ts +8 -4
- package/src/openapi.ts +11 -5
- package/src/pnpm-workspace.ts +1 -1
- package/src/project-predicate.ts +2 -2
- package/src/project.ts +34 -15
- package/src/publish.ts +58 -41
- package/src/release.ts +1 -1
- package/src/scaffold.ts +1 -1
- package/src/tags.ts +15 -18
- package/src/vite.ts +5 -1
- package/src/watch.ts +1 -1
- package/tasks/barrels.ts +3 -3
- package/tasks/clean.ts +2 -2
- package/tasks/openapi.ts +4 -4
- package/tasks/projenrc.ts +3 -3
- package/tasks/sync.ts +1 -1
- package/src/cli-bin.ts +0 -72
- package/tasks/emit.ts +0 -82
package/index.ts
CHANGED
|
@@ -2,55 +2,95 @@
|
|
|
2
2
|
// Regenerated from the exporting modules in ./src.
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
|
-
export * as barrels from "./src/barrels";
|
|
6
|
-
export * as clean from "./src/clean";
|
|
7
|
-
export * as
|
|
8
|
-
export * as
|
|
9
|
-
export * as
|
|
10
|
-
export * as
|
|
11
|
-
export * as
|
|
12
|
-
export * as
|
|
13
|
-
export * as
|
|
14
|
-
export * as
|
|
15
|
-
export * as
|
|
16
|
-
export * as
|
|
17
|
-
export * as
|
|
18
|
-
export * as
|
|
19
|
-
export * as
|
|
20
|
-
export * as
|
|
21
|
-
export * as
|
|
22
|
-
export * as
|
|
23
|
-
export * as
|
|
24
|
-
export * as
|
|
25
|
-
export
|
|
26
|
-
export {
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
29
|
-
export {
|
|
30
|
-
export {
|
|
31
|
-
export
|
|
32
|
-
export {
|
|
33
|
-
export {
|
|
34
|
-
export type {
|
|
35
|
-
export {
|
|
36
|
-
export type {
|
|
37
|
-
export {
|
|
38
|
-
export
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
|
-
export {
|
|
54
|
-
export {
|
|
55
|
-
export {
|
|
56
|
-
|
|
5
|
+
export * as barrels from "./src/barrels.ts";
|
|
6
|
+
export * as clean from "./src/clean.ts";
|
|
7
|
+
export * as codegen from "./src/codegen.ts";
|
|
8
|
+
export * as dbxToolsConfig from "./src/dbx-tools-config.ts";
|
|
9
|
+
export * as engineRoot from "./src/engine-root.ts";
|
|
10
|
+
export * as generated from "./src/generated.ts";
|
|
11
|
+
export * as mixin from "./src/mixin.ts";
|
|
12
|
+
export * as moduleExports from "./src/module-exports.ts";
|
|
13
|
+
export * as openapi from "./src/openapi.ts";
|
|
14
|
+
export * as packages from "./src/packages.ts";
|
|
15
|
+
export * as pnpmWorkspace from "./src/pnpm-workspace.ts";
|
|
16
|
+
export * as project from "./src/project.ts";
|
|
17
|
+
export * as projectPredicate from "./src/project-predicate.ts";
|
|
18
|
+
export * as release from "./src/release.ts";
|
|
19
|
+
export * as scaffold from "./src/scaffold.ts";
|
|
20
|
+
export * as tags from "./src/tags.ts";
|
|
21
|
+
export * as tsconfig from "./src/tsconfig.ts";
|
|
22
|
+
export * as vite from "./src/vite.ts";
|
|
23
|
+
export * as vscode from "./src/vscode.ts";
|
|
24
|
+
export * as watch from "./src/watch.ts";
|
|
25
|
+
export { generateBarrels } from "./src/barrels.ts";
|
|
26
|
+
export { listGeneratedFiles, listNodeModulesDirs, removePaths } from "./src/clean.ts";
|
|
27
|
+
export { generateCodegen } from "./src/codegen.ts";
|
|
28
|
+
export { DBXToolsConfig } from "./src/dbx-tools-config.ts";
|
|
29
|
+
export type { DBXToolsConfigOptions } from "./src/dbx-tools-config.ts";
|
|
30
|
+
export { resolvePkgRoot } from "./src/engine-root.ts";
|
|
31
|
+
export { makeWritable, makeReadonly, isReadonly, header, stampGenerated } from "./src/generated.ts";
|
|
32
|
+
export type { HeaderOpts } from "./src/generated.ts";
|
|
33
|
+
export { create } from "./src/mixin.ts";
|
|
34
|
+
export type { ConstructsMixin } from "./src/mixin.ts";
|
|
35
|
+
export { moduleStatements } from "./src/module-exports.ts";
|
|
36
|
+
export type { ModuleStatement, ModuleExport } from "./src/module-exports.ts";
|
|
37
|
+
export { isTsoaController, generateOpenapi } from "./src/openapi.ts";
|
|
38
|
+
export {
|
|
39
|
+
repoRoot,
|
|
40
|
+
DEFAULT_PACKAGE_ROOTS,
|
|
41
|
+
projectName,
|
|
42
|
+
toPosix,
|
|
43
|
+
isGeneratedFile,
|
|
44
|
+
isModuleFile,
|
|
45
|
+
DiscoveredPackage,
|
|
46
|
+
scanPackages,
|
|
47
|
+
readPackageManifest,
|
|
48
|
+
syncResynthPaths,
|
|
49
|
+
recordedPackages,
|
|
50
|
+
recordedRoots,
|
|
51
|
+
} from "./src/packages.ts";
|
|
52
|
+
export type { RecordedPackage } from "./src/packages.ts";
|
|
53
|
+
export { PnpmWorkspaceState } from "./src/pnpm-workspace.ts";
|
|
54
|
+
export type { Catalog, AllowBuilds, DBXToolsPNPMWorkspaceOptions } from "./src/pnpm-workspace.ts";
|
|
55
|
+
export {
|
|
56
|
+
PackageIdentifier,
|
|
57
|
+
identifier,
|
|
58
|
+
applyCompilerOptions,
|
|
59
|
+
applyIncludes,
|
|
60
|
+
applyTasks,
|
|
61
|
+
applyExports,
|
|
62
|
+
addExports,
|
|
63
|
+
addPackageFiles,
|
|
64
|
+
srcModuleExports,
|
|
65
|
+
PROJEN_VERSION,
|
|
66
|
+
DBXToolsNodeProject,
|
|
67
|
+
DBXToolsTypeScriptProject,
|
|
68
|
+
taskScript,
|
|
69
|
+
applyToProjects,
|
|
70
|
+
} from "./src/project.ts";
|
|
71
|
+
export type {
|
|
72
|
+
DBXToolsProject,
|
|
73
|
+
DBXToolsProjectOptions,
|
|
74
|
+
DBXToolsTypeScriptProjectOptions,
|
|
75
|
+
ApplyToProjectsOptions,
|
|
76
|
+
} from "./src/project.ts";
|
|
77
|
+
export {
|
|
78
|
+
isProject,
|
|
79
|
+
isDBXToolsProject,
|
|
80
|
+
hasName,
|
|
81
|
+
hasIdentifierPackageName,
|
|
82
|
+
hasIdentifierName,
|
|
83
|
+
hasIdentifierScope,
|
|
84
|
+
hasTag,
|
|
85
|
+
hasPath,
|
|
86
|
+
} from "./src/project-predicate.ts";
|
|
87
|
+
export { DBXToolsRelease } from "./src/release.ts";
|
|
88
|
+
export type { StandaloneRelease, DBXToolsReleaseOptions } from "./src/release.ts";
|
|
89
|
+
export { runSynth } from "./src/scaffold.ts";
|
|
90
|
+
export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
|
|
91
|
+
export type { PackageTag } from "./src/tags.ts";
|
|
92
|
+
export { DBXToolsRootTsconfig } from "./src/tsconfig.ts";
|
|
93
|
+
export { ViteConfigFile } from "./src/vite.ts";
|
|
94
|
+
export { DBXToolsVsCode } from "./src/vscode.ts";
|
|
95
|
+
export { watchRoots, watchLoop } from "./src/watch.ts";
|
|
96
|
+
export type { IgnoreGroupOptions } from "./src/watch.ts";
|
package/package.json
CHANGED
package/src/barrels.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* takes priority. This keeps the barrel auto-generated while letting you add or
|
|
16
16
|
* override individual exports.
|
|
17
17
|
*
|
|
18
|
-
* Each eligible module becomes `export * as <name> from "./src/x"` (camelCase
|
|
18
|
+
* Each eligible module becomes `export * as <name> from "./src/x.ts"` (camelCase
|
|
19
19
|
* namespace from its path segments; invalid identifiers suffixed with `Module`),
|
|
20
20
|
* sorted by module path.
|
|
21
21
|
*
|
|
@@ -40,9 +40,15 @@ import { join } from "node:path";
|
|
|
40
40
|
import { find } from "@dbx-tools/path";
|
|
41
41
|
import { string } from "@dbx-tools/shared-core";
|
|
42
42
|
import isIdentifier from "is-identifier";
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
import {
|
|
44
|
+
header,
|
|
45
|
+
makeReadonly,
|
|
46
|
+
makeWritable,
|
|
47
|
+
stampGenerated,
|
|
48
|
+
type HeaderOpts,
|
|
49
|
+
} from "./generated.ts";
|
|
50
|
+
import { moduleExports, moduleStatements, type ModuleExport } from "./module-exports.ts";
|
|
51
|
+
import { isModuleFile, toPosix, recordedPackages } from "./packages.ts";
|
|
46
52
|
|
|
47
53
|
/**
|
|
48
54
|
* A `src`-relative posix path excluded from the root barrel:
|
|
@@ -149,8 +155,7 @@ function hoistUniqueExports(content: string, pkgDir: string, suppress: Set<strin
|
|
|
149
155
|
const seen = new Map<string, { count: number; modulePath: string }>();
|
|
150
156
|
const perModule = new Map<string, ModuleExport[]>();
|
|
151
157
|
for (const { modulePath } of namespaces) {
|
|
152
|
-
const
|
|
153
|
-
const exports = moduleExports(withTsExt(abs));
|
|
158
|
+
const exports = moduleExports(join(pkgDir, modulePath.replace(/^\.\//, "")));
|
|
154
159
|
perModule.set(modulePath, exports);
|
|
155
160
|
for (const { name } of exports) {
|
|
156
161
|
const prior = seen.get(name);
|
|
@@ -177,14 +182,6 @@ function hoistUniqueExports(content: string, pkgDir: string, suppress: Set<strin
|
|
|
177
182
|
return `${content.replace(/\n+$/, "")}\n${lines.join("\n")}\n`;
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
/** Resolve a barrel module path (`./src/x`, extensionless) to its on-disk `.ts(x)` file. */
|
|
181
|
-
function withTsExt(absNoExt: string): string {
|
|
182
|
-
for (const ext of [".ts", ".tsx", ".mts", ".cts"]) {
|
|
183
|
-
if (existsSync(absNoExt + ext)) return absNoExt + ext;
|
|
184
|
-
}
|
|
185
|
-
return `${absNoExt}.ts`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
185
|
/** Hand-authored override barrel: a sibling of the generated `index.ts`. */
|
|
189
186
|
const CUSTOM_EXPORTS_FILE = "exports.ts";
|
|
190
187
|
|
|
@@ -229,7 +226,7 @@ function mergeCustomExports(content: string, pkgDir: string): string {
|
|
|
229
226
|
const ns = /^export \* as (\w+) from /.exec(line)?.[1];
|
|
230
227
|
return !(ns && overridden.has(ns));
|
|
231
228
|
});
|
|
232
|
-
return `${kept.join("\n").replace(/\n+$/, "")}\nexport * from "./exports";\n`;
|
|
229
|
+
return `${kept.join("\n").replace(/\n+$/, "")}\nexport * from "./exports.ts";\n`;
|
|
233
230
|
}
|
|
234
231
|
|
|
235
232
|
/**
|
|
@@ -285,11 +282,14 @@ function generateForPackage(pkgDir: string): number {
|
|
|
285
282
|
// Unlock the read-only barrel so the rewrite below can replace it.
|
|
286
283
|
makeWritable(rootBarrel);
|
|
287
284
|
|
|
288
|
-
// `./src/<path
|
|
289
|
-
// identifiers suffixed with `Module`).
|
|
285
|
+
// `./src/<path>` with the module's REAL extension, namespaced by its path
|
|
286
|
+
// segments (camelCase; invalid identifiers suffixed with `Module`). The
|
|
287
|
+
// extension is written because `tsc` rewrites it on emit
|
|
288
|
+
// (`rewriteRelativeImportExtensions`) - an extensionless specifier would be
|
|
289
|
+
// copied through verbatim and Node's ESM resolver cannot probe for it.
|
|
290
290
|
let content = modulePaths
|
|
291
291
|
.map((stem) => {
|
|
292
|
-
const modulePath = `./src/${stem}`;
|
|
292
|
+
const modulePath = `./src/${byModulePath.get(stem)!}`;
|
|
293
293
|
return `export * as ${modulePathToNamespace(modulePath)} from "${modulePath}";`;
|
|
294
294
|
})
|
|
295
295
|
.join("\n");
|
package/src/clean.ts
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
import { existsSync, rmSync, statSync } from "node:fs";
|
|
25
25
|
import { basename, join, relative } from "node:path";
|
|
26
26
|
import { find } from "@dbx-tools/path";
|
|
27
|
-
import { isReadonly, makeWritable } from "./generated";
|
|
28
|
-
import { repoRoot, toPosix } from "./packages";
|
|
27
|
+
import { isReadonly, makeWritable } from "./generated.ts";
|
|
28
|
+
import { repoRoot, toPosix } from "./packages.ts";
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Basenames `clean` never removes even when they are generated/read-only. `.gitignore`
|
package/src/codegen.ts
CHANGED
|
@@ -47,10 +47,10 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync
|
|
|
47
47
|
import { createRequire } from "node:module";
|
|
48
48
|
import { basename, dirname, join, resolve } from "node:path";
|
|
49
49
|
import type * as ts from "typescript";
|
|
50
|
-
import { lazyRequire } from "./_lazy-require";
|
|
51
|
-
import { header, isReadonly, makeReadonly, makeWritable } from "./generated";
|
|
50
|
+
import { lazyRequire } from "./_lazy-require.ts";
|
|
51
|
+
import { header, isReadonly, makeReadonly, makeWritable } from "./generated.ts";
|
|
52
52
|
import { log, object } from "@dbx-tools/shared-core";
|
|
53
|
-
import { readPackageManifest, repoRoot, recordedPackages } from "./packages";
|
|
53
|
+
import { readPackageManifest, repoRoot, recordedPackages } from "./packages.ts";
|
|
54
54
|
|
|
55
55
|
const logger = log.logger("projen:codegen");
|
|
56
56
|
|
|
@@ -322,8 +322,12 @@ export function generateCodegen(): string[] {
|
|
|
322
322
|
.map((p) => ({ dir: p.dir, inputs: codegenInputs(p.dir) }))
|
|
323
323
|
.filter((t): t is { dir: string; inputs: string[] } => t.inputs !== undefined);
|
|
324
324
|
|
|
325
|
+
// Nothing to generate is the NORMAL case for a workspace that declares no
|
|
326
|
+
// `codegen` inputs, and this runs on every synth - so it stays silent rather
|
|
327
|
+
// than printing a line about work that was never asked for. `debug` keeps it
|
|
328
|
+
// reachable when someone is actually chasing why codegen produced nothing.
|
|
325
329
|
if (targets.length === 0) {
|
|
326
|
-
logger.
|
|
330
|
+
logger.debug("no packages declare a `codegen` field");
|
|
327
331
|
return [];
|
|
328
332
|
}
|
|
329
333
|
|
package/src/openapi.ts
CHANGED
|
@@ -25,8 +25,8 @@ import { createRequire } from "node:module";
|
|
|
25
25
|
import { join } from "node:path";
|
|
26
26
|
import type * as ts from "typescript";
|
|
27
27
|
import { find } from "@dbx-tools/path";
|
|
28
|
-
import { lazyRequire } from "./_lazy-require";
|
|
29
|
-
import { makeReadonly, makeWritable, stampGenerated } from "./generated";
|
|
28
|
+
import { lazyRequire } from "./_lazy-require.ts";
|
|
29
|
+
import { makeReadonly, makeWritable, stampGenerated } from "./generated.ts";
|
|
30
30
|
import { log } from "@dbx-tools/shared-core";
|
|
31
31
|
import {
|
|
32
32
|
type RecordedPackage,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
repoRoot,
|
|
35
35
|
toPosix,
|
|
36
36
|
recordedPackages,
|
|
37
|
-
} from "./packages";
|
|
37
|
+
} from "./packages.ts";
|
|
38
38
|
|
|
39
39
|
const logger = log.logger("projen:openapi");
|
|
40
40
|
|
|
@@ -86,15 +86,21 @@ export function isTsoaController(path: string): boolean {
|
|
|
86
86
|
*/
|
|
87
87
|
export async function generateOpenapi(): Promise<string[]> {
|
|
88
88
|
const pkgs = controllerPackages();
|
|
89
|
+
// Same reasoning as codegen's empty case: a workspace with no tsoa controllers
|
|
90
|
+
// is not a condition worth a line on every synth.
|
|
89
91
|
if (pkgs.length === 0) {
|
|
90
|
-
logger.
|
|
92
|
+
logger.debug("no tsoa controllers found in any server/node package");
|
|
91
93
|
return [];
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
// Lazy, resilient loads: tsoa + typescript are CJS (require), openapi-typescript
|
|
95
97
|
// is ESM (dynamic import).
|
|
96
98
|
const require = createRequire(import.meta.url);
|
|
97
|
-
const { generateSpec } = lazyRequire<typeof import("tsoa")>(
|
|
99
|
+
const { generateSpec } = lazyRequire<typeof import("tsoa")>(
|
|
100
|
+
require,
|
|
101
|
+
"tsoa",
|
|
102
|
+
"openapi generation",
|
|
103
|
+
);
|
|
98
104
|
const tsRuntime = lazyRequire<typeof ts>(require, "typescript", "openapi generation");
|
|
99
105
|
const { default: openapiTS, astToString } = await import("openapi-typescript");
|
|
100
106
|
|
package/src/pnpm-workspace.ts
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
*/
|
|
41
41
|
import { relative } from "node:path";
|
|
42
42
|
import { javascript, type Project } from "projen";
|
|
43
|
-
import { toPosix } from "./packages";
|
|
43
|
+
import { toPosix } from "./packages.ts";
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* The pnpm `catalog:` version registry: dependency name -> version range. A
|
package/src/project-predicate.ts
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
} from "@dbx-tools/shared-core";
|
|
8
8
|
import { IConstruct } from "constructs";
|
|
9
9
|
import { Project } from "projen";
|
|
10
|
-
import { DBXToolsProject, DBXToolsNodeProject, DBXToolsTypeScriptProject } from "./project";
|
|
11
|
-
import { toPosix } from "./packages";
|
|
10
|
+
import { DBXToolsProject, DBXToolsNodeProject, DBXToolsTypeScriptProject } from "./project.ts";
|
|
11
|
+
import { toPosix } from "./packages.ts";
|
|
12
12
|
import { relative } from "node:path";
|
|
13
13
|
import { match, PathMatchInput, PathMatchPredicate } from "@dbx-tools/path";
|
|
14
14
|
import { project } from "..";
|
package/src/project.ts
CHANGED
|
@@ -13,17 +13,17 @@ import { existsSync, readdirSync } from "node:fs";
|
|
|
13
13
|
import { dirname, join, relative, resolve } from "node:path";
|
|
14
14
|
import { Component, IgnoreFile, Project, type TaskOptions, javascript, typescript } from "projen";
|
|
15
15
|
import { ReleaseTrigger } from "projen/lib/release";
|
|
16
|
-
import { generateBarrels } from "./barrels";
|
|
17
|
-
import { generateCodegen } from "./codegen";
|
|
18
|
-
import { DBXToolsConfig, type DBXToolsConfigOptions } from "./dbx-tools-config";
|
|
19
|
-
import { resolvePkgRoot } from "./engine-root";
|
|
20
|
-
import { PnpmWorkspaceState, type DBXToolsPNPMWorkspaceOptions } from "./pnpm-workspace";
|
|
21
|
-
import { applyCompiledPublish } from "./publish";
|
|
22
|
-
import { DBXToolsRelease, type StandaloneRelease } from "./release";
|
|
23
|
-
import { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS, type PackageTag } from "./tags";
|
|
24
|
-
import { DBXToolsRootTsconfig } from "./tsconfig";
|
|
25
|
-
import { ViteConfigFile } from "./vite";
|
|
26
|
-
import { DBXToolsVsCode } from "./vscode";
|
|
16
|
+
import { generateBarrels } from "./barrels.ts";
|
|
17
|
+
import { generateCodegen } from "./codegen.ts";
|
|
18
|
+
import { DBXToolsConfig, type DBXToolsConfigOptions } from "./dbx-tools-config.ts";
|
|
19
|
+
import { resolvePkgRoot } from "./engine-root.ts";
|
|
20
|
+
import { PnpmWorkspaceState, type DBXToolsPNPMWorkspaceOptions } from "./pnpm-workspace.ts";
|
|
21
|
+
import { applyCompiledPublish } from "./publish.ts";
|
|
22
|
+
import { DBXToolsRelease, type StandaloneRelease } from "./release.ts";
|
|
23
|
+
import { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS, type PackageTag } from "./tags.ts";
|
|
24
|
+
import { DBXToolsRootTsconfig } from "./tsconfig.ts";
|
|
25
|
+
import { DEFAULT_VITE_OVERRIDES, ViteConfigFile } from "./vite.ts";
|
|
26
|
+
import { DBXToolsVsCode } from "./vscode.ts";
|
|
27
27
|
import {
|
|
28
28
|
DEFAULT_PACKAGE_ROOTS,
|
|
29
29
|
type DiscoveredPackage,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
repoRoot,
|
|
33
33
|
scanPackages,
|
|
34
34
|
toPosix,
|
|
35
|
-
} from "./packages";
|
|
35
|
+
} from "./packages.ts";
|
|
36
36
|
import { mixin, projectPredicate } from "..";
|
|
37
37
|
import { IConstruct } from "constructs";
|
|
38
38
|
|
|
@@ -271,11 +271,25 @@ export function srcModuleExports(pkg: javascript.NodeProject): Record<string, st
|
|
|
271
271
|
return exports;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
/**
|
|
275
|
-
|
|
274
|
+
/**
|
|
275
|
+
* ESM compiler options every package shares regardless of tag.
|
|
276
|
+
*
|
|
277
|
+
* Relative imports in this repo carry their REAL extension (`./http.ts`), which
|
|
278
|
+
* is what lets `tsc` rewrite them to `./http.js` on emit
|
|
279
|
+
* (`rewriteRelativeImportExtensions`) instead of a post-processing pass fixing up
|
|
280
|
+
* the emitted tree. Both flags belong here rather than on the publishing packages
|
|
281
|
+
* only: the specifier style is a property of the SOURCE, so a `ui` package (which
|
|
282
|
+
* publishes source and is excluded from the compiled surface) still has to accept
|
|
283
|
+
* and rewrite it.
|
|
284
|
+
*/
|
|
285
|
+
const SHARED_COMPILER_OPTIONS: javascript.TypeScriptCompilerOptions & {
|
|
286
|
+
rewriteRelativeImportExtensions: boolean;
|
|
287
|
+
} = {
|
|
276
288
|
module: "ESNext",
|
|
277
289
|
moduleResolution: javascript.TypeScriptModuleResolution.BUNDLER,
|
|
278
290
|
skipLibCheck: true,
|
|
291
|
+
allowImportingTsExtensions: true,
|
|
292
|
+
rewriteRelativeImportExtensions: true,
|
|
279
293
|
};
|
|
280
294
|
|
|
281
295
|
/** Shared formatting rules, applied by projen's Prettier on whichever project is root. */
|
|
@@ -827,7 +841,6 @@ function initProject(
|
|
|
827
841
|
for (const root of roots) {
|
|
828
842
|
project.annotateGenerated(`/${root}/**/index.ts`);
|
|
829
843
|
project.annotateGenerated(`/${root}/openapi/**`);
|
|
830
|
-
project.annotateGenerated(`/${root}/**/bin/*.mjs`);
|
|
831
844
|
}
|
|
832
845
|
|
|
833
846
|
// ESLint lives ONLY on the root and lints every package. `projectService` resolves
|
|
@@ -851,6 +864,12 @@ function initProject(
|
|
|
851
864
|
eslint.addIgnorePattern(`${root}/**/index.ts`);
|
|
852
865
|
}
|
|
853
866
|
eslint.addIgnorePattern("**/vite.config.ts");
|
|
867
|
+
// The unmanaged vite overrides live at the package root, outside any `src/**`
|
|
868
|
+
// tsconfig include, so the type-aware parser cannot resolve them to a project.
|
|
869
|
+
// They are hand-authored (not generated), but ESLint still cannot parse them.
|
|
870
|
+
for (const override of DEFAULT_VITE_OVERRIDES) {
|
|
871
|
+
eslint.addIgnorePattern(`**/${override}`);
|
|
872
|
+
}
|
|
854
873
|
// Codegen packages declare `codegen.inputs` via mixins after construction; ignore
|
|
855
874
|
// their `src/` once manifests are known (preSynthesize), same reason as openapi.
|
|
856
875
|
new EslintIgnoreCodegen(project);
|
package/src/publish.ts
CHANGED
|
@@ -4,44 +4,52 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Packages resolve each other from SOURCE - every `exports` entry points at a
|
|
6
6
|
* `.ts` file, so a cross-package import type-checks with no build step and no
|
|
7
|
-
* `
|
|
7
|
+
* `lib` to keep in sync. That property is worth keeping, but it cannot be what
|
|
8
8
|
* ships: Node refuses to strip types under `node_modules`
|
|
9
9
|
* (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`), so a published package whose
|
|
10
10
|
* entry point is `index.ts` is unloadable by anything that is not a bundler.
|
|
11
|
-
* Consumers papered over that by special-casing `@dbx-tools/*` into their own
|
|
12
|
-
* bundle, which is a tax this repo has no business charging.
|
|
13
11
|
*
|
|
14
12
|
* pnpm resolves both at once. It substitutes `publishConfig`'s `main`/`types`/
|
|
15
|
-
* `exports` into the manifest at pack time and drops `publishConfig`
|
|
16
|
-
* the workspace keeps its source entry points while the tarball
|
|
17
|
-
* compiled ones. Nothing here changes how the repo builds or
|
|
18
|
-
* only changes what `pnpm pack` writes.
|
|
13
|
+
* `bin`/`exports` into the manifest at pack time and drops `publishConfig`
|
|
14
|
+
* itself, so the workspace keeps its source entry points while the tarball
|
|
15
|
+
* advertises the compiled ones. Nothing here changes how the repo builds or
|
|
16
|
+
* type-checks; it only changes what `pnpm pack` writes.
|
|
19
17
|
*
|
|
20
|
-
* Two
|
|
18
|
+
* Two compiler options make the emitted tree loadable, both native `tsc`:
|
|
21
19
|
*
|
|
22
20
|
* - `rootDir: "."` so the package-ROOT `index.ts` barrel is compiled at all.
|
|
23
21
|
* projen's default `rootDir: "src"` puts the barrel outside the compilation,
|
|
24
|
-
* which is why `lib/`
|
|
25
|
-
* -
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* sites free of the `.js` suffix that would otherwise have to be written - and
|
|
30
|
-
* maintained - by hand.
|
|
22
|
+
* which is why `lib/` would otherwise have no `index.js`.
|
|
23
|
+
* - `rewriteRelativeImportExtensions`, which turns the `./x.ts` specifiers this
|
|
24
|
+
* repo writes into the `./x.js` Node's ESM resolver needs. Sources carry the
|
|
25
|
+
* real extension (`allowImportingTsExtensions`) and `tsc` rewrites it on emit,
|
|
26
|
+
* so no post-processing pass is involved.
|
|
31
27
|
*
|
|
32
28
|
* UI packages are deliberately excluded (see {@link publishesCompiled}).
|
|
33
29
|
*/
|
|
34
30
|
import type { javascript } from "projen";
|
|
35
31
|
import { typescript } from "projen";
|
|
36
|
-
import { addPackageFiles, applyCompilerOptions, applyIncludes
|
|
37
|
-
import { isDBXToolsProject } from "./project-predicate";
|
|
32
|
+
import { addPackageFiles, applyCompilerOptions, applyIncludes } from "./project.ts";
|
|
33
|
+
import { isDBXToolsProject } from "./project-predicate.ts";
|
|
38
34
|
|
|
39
35
|
/** Directory `tsc` emits into, and the root of every published entry point. */
|
|
40
36
|
export const COMPILED_DIR = "lib";
|
|
41
37
|
|
|
42
|
-
/** An `exports` target written as TypeScript source, i.e.
|
|
38
|
+
/** An `exports`/`bin` target written as TypeScript source, i.e. with a compiled twin. */
|
|
43
39
|
const TS_SOURCE = /\.tsx?$/;
|
|
44
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Compiler options that make a package's emitted `lib/` tree loadable by Node.
|
|
43
|
+
*
|
|
44
|
+
* Only `rootDir` here: the specifier rewriting
|
|
45
|
+
* (`allowImportingTsExtensions` + `rewriteRelativeImportExtensions`) is part of
|
|
46
|
+
* the tsconfig floor EVERY package gets, since the `.ts`-suffixed specifier style
|
|
47
|
+
* is a property of the source rather than of publishing.
|
|
48
|
+
*/
|
|
49
|
+
export const COMPILED_COMPILER_OPTIONS: javascript.TypeScriptCompilerOptions = {
|
|
50
|
+
rootDir: ".",
|
|
51
|
+
};
|
|
52
|
+
|
|
45
53
|
/**
|
|
46
54
|
* Whether a package publishes compiled output rather than source.
|
|
47
55
|
*
|
|
@@ -52,15 +60,16 @@ const TS_SOURCE = /\.tsx?$/;
|
|
|
52
60
|
* `./styles.css` plus raw SVG assets that `tsc` does not copy and could not
|
|
53
61
|
* rewrite. Compiling them would mean a real asset pipeline (Vite library mode)
|
|
54
62
|
* to solve a problem they do not have.
|
|
55
|
-
*
|
|
56
|
-
* This is the same split the downstream app build already makes on its own: its
|
|
57
|
-
* client bundle resolves `@dbx-tools/ui-*` from source without complaint, while
|
|
58
|
-
* its SERVER bundle is the one that had to inline `@dbx-tools/*` to avoid
|
|
59
|
-
* handing Node a `.ts` entry point.
|
|
60
63
|
*/
|
|
61
64
|
export function publishesCompiled(pkg: javascript.NodeProject): boolean {
|
|
62
65
|
if (!(pkg instanceof typescript.TypeScriptProject) || !pkg.parent) return false;
|
|
63
|
-
return isDBXToolsProject(pkg) && !pkg.dbxToolsConfig.tags.includes("ui");
|
|
66
|
+
return isDBXToolsProject()(pkg) && !pkg.dbxToolsConfig.tags.includes("ui");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The `./lib/...` stem of a source path, or `undefined` if it is not TypeScript. */
|
|
70
|
+
function compiledStem(target: string): string | undefined {
|
|
71
|
+
if (!TS_SOURCE.test(target)) return undefined;
|
|
72
|
+
return `./${COMPILED_DIR}/${target.replace(/^\.\//, "").replace(TS_SOURCE, "")}`;
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
/**
|
|
@@ -71,13 +80,12 @@ export function publishesCompiled(pkg: javascript.NodeProject): boolean {
|
|
|
71
80
|
* mapping is positional: `./src/react/index.ts` -> `./lib/src/react/index.js`.
|
|
72
81
|
*/
|
|
73
82
|
function compiledTarget(target: string): { types: string; default: string } | undefined {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return { types: `${stem}.d.ts`, default: `${stem}.js` };
|
|
83
|
+
const stem = compiledStem(target);
|
|
84
|
+
return stem ? { types: `${stem}.d.ts`, default: `${stem}.js` } : undefined;
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
/**
|
|
80
|
-
* Derive `publishConfig` from the package's FINAL `exports`
|
|
88
|
+
* Derive `publishConfig` from the package's FINAL `exports` + `bin` maps.
|
|
81
89
|
*
|
|
82
90
|
* Runs at preSynthesize precisely so the tags have already installed their
|
|
83
91
|
* export layouts - deriving it any earlier would mirror the constructor's bare
|
|
@@ -88,9 +96,26 @@ function publishConfig(pkg: javascript.NodeProject): Record<string, unknown> | u
|
|
|
88
96
|
const compiled = Object.entries(exports).map(
|
|
89
97
|
([subpath, target]) => [subpath, compiledTarget(target) ?? target] as const,
|
|
90
98
|
);
|
|
99
|
+
|
|
100
|
+
// A CLI's `bin` points at its `.ts` entry in-repo (Node strips types outside
|
|
101
|
+
// `node_modules`, so a workspace checkout runs it directly); the tarball has to
|
|
102
|
+
// point at the emitted `.js`, which is what lets the published CLI run with no
|
|
103
|
+
// loader installed.
|
|
104
|
+
// `bin` is rendered LAZILY (projen assigns `bin: () => this.renderBin()` and
|
|
105
|
+
// keeps the map itself private), so unlike `exports` this has to be called -
|
|
106
|
+
// reading the field directly yields the function and finds no entries.
|
|
107
|
+
const binField = pkg.package.manifest.bin as
|
|
108
|
+
Record<string, string> | (() => Record<string, string>) | undefined;
|
|
109
|
+
const bin = (typeof binField === "function" ? binField() : binField) ?? {};
|
|
110
|
+
const compiledBin = Object.entries(bin).flatMap(([name, target]) => {
|
|
111
|
+
const stem = compiledStem(target);
|
|
112
|
+
return stem ? [[name, `${stem}.js`] as const] : [];
|
|
113
|
+
});
|
|
114
|
+
|
|
91
115
|
// Nothing to swap means the package ships no TypeScript entry point at all;
|
|
92
116
|
// leave its manifest alone rather than writing an inert `publishConfig`.
|
|
93
|
-
|
|
117
|
+
const swaps = compiled.some(([, target]) => typeof target !== "string");
|
|
118
|
+
if (!swaps && compiledBin.length === 0) return undefined;
|
|
94
119
|
|
|
95
120
|
// Setting this field REPLACES whatever projen renders into it, and what projen
|
|
96
121
|
// renders is `access` - dropped, every scoped package here would publish as
|
|
@@ -100,13 +125,14 @@ function publishConfig(pkg: javascript.NodeProject): Record<string, unknown> | u
|
|
|
100
125
|
return {
|
|
101
126
|
access: pkg.package.npmAccess,
|
|
102
127
|
...(typeof root === "object" ? { main: root.default, types: root.types } : {}),
|
|
128
|
+
...(compiledBin.length ? { bin: Object.fromEntries(compiledBin) } : {}),
|
|
103
129
|
exports: Object.fromEntries(compiled),
|
|
104
130
|
};
|
|
105
131
|
}
|
|
106
132
|
|
|
107
133
|
/**
|
|
108
|
-
* Give a package a compiled publish surface: emit the barrel,
|
|
109
|
-
*
|
|
134
|
+
* Give a package a compiled publish surface: emit the barrel, ship `lib/`, and
|
|
135
|
+
* advertise it through `publishConfig`.
|
|
110
136
|
*
|
|
111
137
|
* Idempotent - `preSynthesizeProject` reaches every package twice (once from the
|
|
112
138
|
* root's walk, once from the package's own preSynthesize) and both passes must
|
|
@@ -115,22 +141,13 @@ function publishConfig(pkg: javascript.NodeProject): Record<string, unknown> | u
|
|
|
115
141
|
export function applyCompiledPublish(pkg: javascript.NodeProject): void {
|
|
116
142
|
if (!publishesCompiled(pkg)) return;
|
|
117
143
|
|
|
118
|
-
|
|
119
|
-
// The `cli` tag already does this for its `bin/` tree; for everything else it
|
|
120
|
-
// is what puts an `index.js` in the emitted output for the first time.
|
|
121
|
-
applyCompilerOptions(pkg, { rootDir: "." });
|
|
144
|
+
applyCompilerOptions(pkg, COMPILED_COMPILER_OPTIONS);
|
|
122
145
|
applyIncludes(pkg, "index.ts");
|
|
123
146
|
addPackageFiles(pkg, COMPILED_DIR);
|
|
124
147
|
|
|
125
148
|
const config = publishConfig(pkg);
|
|
126
149
|
if (config) pkg.package.addField("publishConfig", config);
|
|
127
150
|
|
|
128
|
-
// `tsc` emits extensionless relative specifiers; Node ESM cannot resolve them.
|
|
129
|
-
const fixSpecifiers = taskScript(pkg, "emit.ts", COMPILED_DIR);
|
|
130
|
-
if (!pkg.compileTask.steps.some((step) => step.exec === fixSpecifiers)) {
|
|
131
|
-
pkg.compileTask.exec(fixSpecifiers);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
151
|
// The release workflow publishes straight after `pnpm install`, with no build
|
|
135
152
|
// in between, so the compiled output has to be produced by the pack itself
|
|
136
153
|
// rather than assumed present. This also covers a bare `pnpm pack` and the
|
package/src/release.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { Component } from "projen";
|
|
8
8
|
import { GithubWorkflow } from "projen/lib/github";
|
|
9
9
|
import { JobPermission } from "projen/lib/github/workflows-model";
|
|
10
|
-
import { applyTasks, taskScript, type DBXToolsNodeProject } from "./project";
|
|
10
|
+
import { applyTasks, taskScript, type DBXToolsNodeProject } from "./project.ts";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* A standalone project that lives in a repo SUBDIRECTORY but is NOT a member of
|
package/src/scaffold.ts
CHANGED
package/src/tags.ts
CHANGED
|
@@ -14,9 +14,8 @@
|
|
|
14
14
|
* callers add their own afterward.
|
|
15
15
|
*/
|
|
16
16
|
import type { IMixin as ConstructsMixin } from "constructs";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { create } from "./mixin";
|
|
17
|
+
import { javascript } from "projen";
|
|
18
|
+
import { create } from "./mixin.ts";
|
|
20
19
|
import {
|
|
21
20
|
addPackageFiles,
|
|
22
21
|
applyCompilerOptions,
|
|
@@ -24,9 +23,9 @@ import {
|
|
|
24
23
|
applyIncludes,
|
|
25
24
|
applyTasks,
|
|
26
25
|
srcModuleExports,
|
|
27
|
-
} from "./project";
|
|
28
|
-
import * as projectPredicate from "./project-predicate";
|
|
29
|
-
import { ViteConfigFile } from "./vite";
|
|
26
|
+
} from "./project.ts";
|
|
27
|
+
import * as projectPredicate from "./project-predicate.ts";
|
|
28
|
+
import { ViteConfigFile } from "./vite.ts";
|
|
30
29
|
|
|
31
30
|
/** Node compiler options: ES2022 lib + node types, deliberately no DOM. */
|
|
32
31
|
const NODE_COMPILER_OPTIONS: javascript.TypeScriptCompilerOptions = {
|
|
@@ -114,19 +113,17 @@ export const PACKAGE_TAG_MIXINS = {
|
|
|
114
113
|
});
|
|
115
114
|
}),
|
|
116
115
|
cli: create(projectPredicate.hasTag("cli"), (p) => {
|
|
117
|
-
// tsx
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
p.addDeps("commander@catalog:", "@clack/prompts@catalog:"
|
|
116
|
+
// No tsx: a CLI's `bin` resolves to its emitted `lib/bin/<name>.js` in the
|
|
117
|
+
// published tarball (see `publishConfig` in {@link applyCompiledPublish}), so
|
|
118
|
+
// the installed CLI is plain JavaScript Node runs directly - no loader to
|
|
119
|
+
// register and no launcher shim to generate. In a WORKSPACE checkout the
|
|
120
|
+
// manifest still points at the `.ts` entry, which Node type-strips on its own
|
|
121
|
+
// because the package is not under `node_modules`.
|
|
122
|
+
p.addDeps("commander@catalog:", "@clack/prompts@catalog:");
|
|
124
123
|
p.addDevDeps("@types/node@catalog:");
|
|
125
|
-
addCliBinLaunchers(p);
|
|
126
124
|
// A CLI compiles code OUTSIDE `src/` - its root `index.ts` barrel and the
|
|
127
|
-
// `bin/` entries - which the src-only tag default doesn't reach
|
|
128
|
-
|
|
129
|
-
applyCompilerOptions(p, { ...NODE_COMPILER_OPTIONS, rootDir: "." });
|
|
125
|
+
// `bin/` entries - which the src-only tag default doesn't reach.
|
|
126
|
+
applyCompilerOptions(p, NODE_COMPILER_OPTIONS);
|
|
130
127
|
applyIncludes(p, "index.ts", "bin/**/*.ts");
|
|
131
128
|
// A CLI's standard surface: the `.` root entry, a `./<module>` subpath per
|
|
132
129
|
// top-level `src` module, and `./package.json`. Derived rather than declared
|
|
@@ -137,7 +134,7 @@ export const PACKAGE_TAG_MIXINS = {
|
|
|
137
134
|
"./package.json": "./package.json",
|
|
138
135
|
});
|
|
139
136
|
// A CLI is the one shape whose entry point lives outside `src`, so its
|
|
140
|
-
//
|
|
137
|
+
// `bin/` tree has to be added to the tarball allowlist explicitly.
|
|
141
138
|
addPackageFiles(p, "bin");
|
|
142
139
|
}),
|
|
143
140
|
server: create(projectPredicate.hasTag("server"), (p) => {
|
package/src/vite.ts
CHANGED
|
@@ -24,8 +24,12 @@ import { type Project, TextFile } from "projen";
|
|
|
24
24
|
* (later wins, absent files skipped). Both extensions are accepted so an existing
|
|
25
25
|
* JavaScript override keeps working; the TypeScript one is listed last so it wins
|
|
26
26
|
* where a package is mid-migration and still has both.
|
|
27
|
+
*
|
|
28
|
+
* Exported because ESLint has to ignore them: an override is a package-ROOT file
|
|
29
|
+
* outside any `src/**` tsconfig include, so the type-aware parser cannot resolve
|
|
30
|
+
* it to a project - the same reason the generated `vite.config.ts` is ignored.
|
|
27
31
|
*/
|
|
28
|
-
const DEFAULT_VITE_OVERRIDES = ["vite.config.override.js", "vite.config.override.ts"];
|
|
32
|
+
export const DEFAULT_VITE_OVERRIDES = ["vite.config.override.js", "vite.config.override.ts"];
|
|
29
33
|
|
|
30
34
|
/** Render the generated `vite.config.ts` source with the override chain inlined. */
|
|
31
35
|
function renderViteConfig(overridePaths: string[]): string {
|
package/src/watch.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { isAbsolute, resolve } from "node:path";
|
|
18
18
|
import { watch as fileScan } from "@dbx-tools/path";
|
|
19
19
|
import { log } from "@dbx-tools/shared-core";
|
|
20
|
-
import { isGeneratedFile, recordedRoots, repoRoot } from "./packages";
|
|
20
|
+
import { isGeneratedFile, recordedRoots, repoRoot } from "./packages.ts";
|
|
21
21
|
|
|
22
22
|
const logger = log.logger("projen:watch");
|
|
23
23
|
const DEBOUNCE_MS = 250;
|
package/tasks/barrels.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { sep } from "node:path";
|
|
3
|
-
import { generateBarrels } from "../src/barrels";
|
|
3
|
+
import { generateBarrels } from "../src/barrels.ts";
|
|
4
4
|
import { log, string } from "@dbx-tools/shared-core";
|
|
5
|
-
import { watchLoop, watchRoots } from "../src/watch";
|
|
6
|
-
import { recordedPackages } from "../src/packages";
|
|
5
|
+
import { watchLoop, watchRoots } from "../src/watch.ts";
|
|
6
|
+
import { recordedPackages } from "../src/packages.ts";
|
|
7
7
|
|
|
8
8
|
const logger = log.logger("projen:barrels");
|
|
9
9
|
|
package/tasks/clean.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { relative } from "node:path";
|
|
3
|
-
import { listGeneratedFiles, listNodeModulesDirs, removePaths } from "../src/clean";
|
|
3
|
+
import { listGeneratedFiles, listNodeModulesDirs, removePaths } from "../src/clean.ts";
|
|
4
4
|
import { log, string } from "@dbx-tools/shared-core";
|
|
5
|
-
import { repoRoot, toPosix } from "../src/packages";
|
|
5
|
+
import { repoRoot, toPosix } from "../src/packages.ts";
|
|
6
6
|
|
|
7
7
|
const logger = log.logger("projen:clean");
|
|
8
8
|
const yes = process.argv.includes("-y") || process.argv.includes("--yes");
|
package/tasks/openapi.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
|
-
import { generateBarrels } from "../src/barrels";
|
|
2
|
+
import { generateBarrels } from "../src/barrels.ts";
|
|
3
3
|
import { log, string } from "@dbx-tools/shared-core";
|
|
4
|
-
import { generateOpenapi, isTsoaController } from "../src/openapi";
|
|
5
|
-
import { runSynth } from "../src/scaffold";
|
|
6
|
-
import { watchLoop, watchRoots } from "../src/watch";
|
|
4
|
+
import { generateOpenapi, isTsoaController } from "../src/openapi.ts";
|
|
5
|
+
import { runSynth } from "../src/scaffold.ts";
|
|
6
|
+
import { watchLoop, watchRoots } from "../src/watch.ts";
|
|
7
7
|
|
|
8
8
|
const logger = log.logger("projen:openapi");
|
|
9
9
|
|
package/tasks/projenrc.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { log } from "@dbx-tools/shared-core";
|
|
4
|
-
import { runSynth } from "../src/scaffold";
|
|
5
|
-
import { watchLoop } from "../src/watch";
|
|
6
|
-
import { repoRoot, syncResynthPaths } from "../src/packages";
|
|
4
|
+
import { runSynth } from "../src/scaffold.ts";
|
|
5
|
+
import { watchLoop } from "../src/watch.ts";
|
|
6
|
+
import { repoRoot, syncResynthPaths } from "../src/packages.ts";
|
|
7
7
|
|
|
8
8
|
const logger = log.logger("projen:projenrc");
|
|
9
9
|
|
package/tasks/sync.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import concurrently from "concurrently";
|
|
4
4
|
import { log } from "@dbx-tools/shared-core";
|
|
5
|
-
import { runSynth } from "../src/scaffold";
|
|
5
|
+
import { runSynth } from "../src/scaffold.ts";
|
|
6
6
|
|
|
7
7
|
const logger = log.logger("projen:sync");
|
|
8
8
|
|
package/src/cli-bin.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generated `bin/<name>.mjs` launchers for a CLI package's TypeScript entries.
|
|
3
|
-
*
|
|
4
|
-
* A CLI's real entry is a `.ts` file, which Node cannot run on its own, so something
|
|
5
|
-
* has to register tsx first. A `#!/usr/bin/env -S npx tsx` shebang does that only for
|
|
6
|
-
* a WORKSPACE checkout: `npx` resolves tsx from the current working directory, and a
|
|
7
|
-
* globally installed CLI (`npm i -g @dbx-tools/cli`) has no relationship to whatever
|
|
8
|
-
* directory the user happens to be in - so every first invocation stalls to fetch tsx
|
|
9
|
-
* from the network, or fails outright when offline.
|
|
10
|
-
*
|
|
11
|
-
* {@link CliBinLauncher} emits a tiny `.mjs` sibling that npm points its bin symlink
|
|
12
|
-
* at. Because the launcher reaches tsx through a bare `import` specifier, NODE
|
|
13
|
-
* resolves it relative to the launcher's own location - i.e. the CLI package's own
|
|
14
|
-
* `node_modules` - which is exactly where the `cli` tag's runtime tsx dependency
|
|
15
|
-
* installs it. The `.ts` entry stays the source of truth; the launcher only registers
|
|
16
|
-
* the loader and hands off.
|
|
17
|
-
*
|
|
18
|
-
* {@link addCliBinLaunchers} discovers the entries by scanning the package's `bin/`
|
|
19
|
-
* directory at synth, so a new CLI just needs its `.ts` file plus a `package.json`
|
|
20
|
-
* bin pointing at the matching `.mjs`.
|
|
21
|
-
*/
|
|
22
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
23
|
-
import { join } from "node:path";
|
|
24
|
-
import { type Project, TextFile } from "projen";
|
|
25
|
-
import { header } from "./generated";
|
|
26
|
-
|
|
27
|
-
/** Directory (package-relative) holding a CLI's executable entries. */
|
|
28
|
-
const BIN_DIR = "bin";
|
|
29
|
-
|
|
30
|
-
/** Render the launcher source for a `.ts` entry sitting beside it. */
|
|
31
|
-
function renderLauncher(entryFileName: string): string {
|
|
32
|
-
return `#!/usr/bin/env node
|
|
33
|
-
${header({
|
|
34
|
-
tool: "projen synth (cli tag)",
|
|
35
|
-
source: `./${entryFileName}`,
|
|
36
|
-
})}
|
|
37
|
-
// Resolved as a bare specifier so Node looks in THIS file's package rather than the
|
|
38
|
-
// caller's cwd - the only way a globally installed CLI finds its own tsx.
|
|
39
|
-
import { register } from "tsx/esm/api";
|
|
40
|
-
|
|
41
|
-
register();
|
|
42
|
-
await import(new URL(${JSON.stringify(`./${entryFileName}`)}, import.meta.url).href);
|
|
43
|
-
`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* A projen-owned, read-only `.mjs` launcher that registers tsx and defers to the
|
|
48
|
-
* `.ts` entry beside it.
|
|
49
|
-
*/
|
|
50
|
-
export class CliBinLauncher extends TextFile {
|
|
51
|
-
constructor(project: Project, entryFileName: string) {
|
|
52
|
-
super(project, join(BIN_DIR, entryFileName.replace(/\.ts$/, ".mjs")), {
|
|
53
|
-
readonly: true,
|
|
54
|
-
executable: true,
|
|
55
|
-
lines: renderLauncher(entryFileName).split("\n"),
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Emit a {@link CliBinLauncher} for every `.ts` entry in the package's `bin/`
|
|
62
|
-
* directory. A package with no `bin/` directory is left alone.
|
|
63
|
-
*/
|
|
64
|
-
export function addCliBinLaunchers(project: Project): void {
|
|
65
|
-
const binDir = join(project.outdir, BIN_DIR);
|
|
66
|
-
if (!existsSync(binDir)) return;
|
|
67
|
-
|
|
68
|
-
for (const entry of readdirSync(binDir).sort()) {
|
|
69
|
-
if (!entry.endsWith(".ts") || entry.endsWith(".d.ts")) continue;
|
|
70
|
-
new CliBinLauncher(project, entry);
|
|
71
|
-
}
|
|
72
|
-
}
|
package/tasks/emit.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Append the explicit file extension Node ESM requires to every relative
|
|
3
|
-
* specifier in a compiled tree.
|
|
4
|
-
*
|
|
5
|
-
* Sources are written for `moduleResolution: bundler`, so they import `"./http"`
|
|
6
|
-
* with no extension, and `tsc` copies specifiers through untouched. Node's ESM
|
|
7
|
-
* resolver does no extension probing, so that emitted output is unloadable -
|
|
8
|
-
* which is the whole reason these packages used to publish source instead. The
|
|
9
|
-
* alternative fix is to write `"./http.js"` at all 800-odd import sites and keep
|
|
10
|
-
* writing it forever; this pass makes it a build detail instead.
|
|
11
|
-
*
|
|
12
|
-
* Deliberately filesystem-driven rather than clever: each specifier is resolved
|
|
13
|
-
* against what `tsc` actually emitted, so a module that became a file and a
|
|
14
|
-
* module that became a directory are told apart by looking, not by guessing.
|
|
15
|
-
* Anything that does not resolve is left exactly as it was - a bare package
|
|
16
|
-
* specifier, an asset, or a genuine mistake that should surface as itself.
|
|
17
|
-
*
|
|
18
|
-
* Usage: `tsx emit.ts <compiled-dir>` (relative to the package root).
|
|
19
|
-
*/
|
|
20
|
-
import { log } from "@dbx-tools/shared-core";
|
|
21
|
-
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
22
|
-
import { dirname, join, resolve } from "node:path";
|
|
23
|
-
|
|
24
|
-
const logger = log.logger("projen:emit");
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The specifier of a static import/export or a dynamic `import()`.
|
|
28
|
-
*
|
|
29
|
-
* Anchored on the keyword that precedes it so a relative path appearing inside a
|
|
30
|
-
* string literal or a comment is never touched.
|
|
31
|
-
*/
|
|
32
|
-
const SPECIFIER = /(\bfrom\s*|\bimport\s*\(\s*|\brequire\s*\(\s*)(["'])(\.\.?\/[^"']*)\2/g;
|
|
33
|
-
|
|
34
|
-
/** Extensions that are already explicit, whether JavaScript or an asset. */
|
|
35
|
-
const EXPLICIT = /\.(js|mjs|cjs|json|css|svg|png|jpg|jpeg|gif|webp)$/;
|
|
36
|
-
|
|
37
|
-
/** Files whose specifiers matter: the emitted JavaScript and its declarations. */
|
|
38
|
-
const EMITTED = /\.(js|mjs|cjs|d\.ts)$/;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* What a relative specifier must become, or `undefined` to leave it alone.
|
|
42
|
-
*
|
|
43
|
-
* Declarations resolve against the emitted `.js` too: inside a `.d.ts`,
|
|
44
|
-
* TypeScript maps a `"./foo.js"` specifier onto the neighbouring `foo.d.ts`, so
|
|
45
|
-
* both file kinds want the same suffix.
|
|
46
|
-
*/
|
|
47
|
-
function retarget(fileDir: string, specifier: string): string | undefined {
|
|
48
|
-
if (EXPLICIT.test(specifier)) return undefined;
|
|
49
|
-
const base = resolve(fileDir, specifier);
|
|
50
|
-
if (existsSync(`${base}.js`)) return `${specifier}.js`;
|
|
51
|
-
if (existsSync(join(base, "index.js"))) return `${specifier}/index.js`;
|
|
52
|
-
return undefined;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Every file under `dir`, recursively. */
|
|
56
|
-
function walk(dir: string): string[] {
|
|
57
|
-
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) =>
|
|
58
|
-
entry.isDirectory() ? walk(join(dir, entry.name)) : [join(dir, entry.name)],
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Rewrite one file in place; returns whether anything changed. */
|
|
63
|
-
function rewrite(file: string): boolean {
|
|
64
|
-
const before = readFileSync(file, "utf8");
|
|
65
|
-
const after = before.replace(SPECIFIER, (match, keyword, quote, specifier) => {
|
|
66
|
-
const next = retarget(dirname(file), specifier as string);
|
|
67
|
-
return next ? `${keyword}${quote}${next}${quote}` : match;
|
|
68
|
-
});
|
|
69
|
-
if (after === before) return false;
|
|
70
|
-
writeFileSync(file, after);
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const target = resolve(process.argv[2] ?? "lib");
|
|
75
|
-
// A package whose compile produced nothing (no emit, or a tag that replaced the
|
|
76
|
-
// compile task) is not an error - there is simply nothing to fix up.
|
|
77
|
-
if (existsSync(target)) {
|
|
78
|
-
const changed = walk(target)
|
|
79
|
-
.filter((file) => EMITTED.test(file))
|
|
80
|
-
.filter(rewrite).length;
|
|
81
|
-
if (changed > 0) logger.debug(`emit: resolved specifiers in ${changed} files`);
|
|
82
|
-
}
|