@aws/nx-plugin 1.0.0-rc.93 → 1.0.0-rc.94
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/migrations.json +16 -1
- package/package.json +1 -1
- package/src/agentcore-harness/generator.d.ts +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/infra/app/generator.d.ts +1 -1
- package/src/infra/app/generator.js +5 -3
- package/src/infra/app/generator.js.map +1 -1
- package/src/init/generator.d.ts +1 -1
- package/src/migrations/latest/agent-core-vendor-target/metadata.json +3 -0
- package/src/migrations/latest/agent-core-vendor-target/migration.d.ts +6 -0
- package/src/migrations/latest/agent-core-vendor-target/migration.js +98 -0
- package/src/migrations/latest/agent-core-vendor-target/migration.js.map +1 -0
- package/src/migrations/latest/infra-deploy-express-mode/metadata.json +3 -0
- package/src/migrations/latest/infra-deploy-express-mode/migration.d.ts +6 -0
- package/src/migrations/latest/infra-deploy-express-mode/migration.js +83 -0
- package/src/migrations/latest/infra-deploy-express-mode/migration.js.map +1 -0
- package/src/migrations/latest/shx-fs-commands/metadata.json +3 -0
- package/src/migrations/latest/shx-fs-commands/migration.d.ts +6 -0
- package/src/migrations/latest/shx-fs-commands/migration.js +216 -0
- package/src/migrations/latest/shx-fs-commands/migration.js.map +1 -0
- package/src/open-api/ts-client/__snapshots__/generator.complex-types.spec.ts.snap +18 -12
- package/src/open-api/ts-client/__snapshots__/generator.edge-cases.spec.ts.snap +5 -2
- package/src/open-api/utils/codegen-data/types.d.ts +6 -0
- package/src/open-api/utils/codegen-data/types.js.map +1 -1
- package/src/open-api/utils/codegen-data.js +32 -7
- package/src/open-api/utils/codegen-data.js.map +1 -1
- package/src/open-api/utils/normalise.js +39 -14
- package/src/open-api/utils/normalise.js.map +1 -1
- package/src/open-api/utils/parser.js +51 -5
- package/src/open-api/utils/parser.js.map +1 -1
- package/src/preset/generator.d.ts +1 -1
- package/src/py/agent/a2a-connection/generator.d.ts +1 -1
- package/src/py/agent/gateway-connection/generator.d.ts +1 -1
- package/src/py/agent/generator.d.ts +2 -26
- package/src/py/agent/generator.js +2 -2
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/agent/mcp-connection/generator.d.ts +1 -1
- package/src/py/agent/react-connection/generator.d.ts +1 -1
- package/src/py/fast-api/generator.d.ts +1 -13
- package/src/py/fast-api/generator.js +1 -1
- package/src/py/fast-api/generator.js.map +1 -1
- package/src/py/fast-api/react/generator.d.ts +1 -1
- package/src/py/mcp-server/generator.d.ts +2 -26
- package/src/py/mcp-server/generator.js +2 -2
- package/src/py/mcp-server/generator.js.map +1 -1
- package/src/py/rdb/generator.d.ts +2 -26
- package/src/py/rdb/generator.js +6 -6
- package/src/py/rdb/generator.js.map +1 -1
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +24 -20
- package/src/smithy/project/generator.d.ts +2 -14
- package/src/smithy/project/generator.js +2 -1
- package/src/smithy/project/generator.js.map +1 -1
- package/src/smithy/react-connection/generator.d.ts +1 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +8 -7
- package/src/smithy/ts/api/generator.d.ts +1 -13
- package/src/smithy/ts/api/generator.js +1 -1
- package/src/smithy/ts/api/generator.js.map +1 -1
- package/src/terraform/project/generator.d.ts +2 -2
- package/src/terraform/project/generator.js +4 -4
- package/src/terraform/project/generator.js.map +1 -1
- package/src/trpc/react/generator.d.ts +1 -1
- package/src/ts/agent/a2a-connection/generator.d.ts +1 -1
- package/src/ts/agent/gateway-connection/generator.d.ts +1 -1
- package/src/ts/agent/generator.d.ts +3 -39
- package/src/ts/agent/generator.js +1 -1
- package/src/ts/agent/generator.js.map +1 -1
- package/src/ts/agent/mcp-connection/generator.d.ts +1 -1
- package/src/ts/agent/react-connection/generator.d.ts +1 -1
- package/src/ts/astro-docs/generator.d.ts +1 -1
- package/src/ts/dcr-proxy/generator.d.ts +1 -1
- package/src/ts/dynamodb/generator.d.ts +1 -1
- package/src/ts/lambda-function/generator.d.ts +1 -1
- package/src/ts/lib/generator.d.ts +1 -1
- package/src/ts/mcp-server/generator.d.ts +3 -39
- package/src/ts/mcp-server/generator.js +1 -1
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/nx-generator/generator.d.ts +1 -1
- package/src/ts/nx-migration/generator.d.ts +1 -1
- package/src/ts/nx-plugin/generator.d.ts +1 -1
- package/src/ts/rdb/generator.d.ts +3 -27
- package/src/ts/rdb/generator.js +3 -3
- package/src/ts/rdb/generator.js.map +1 -1
- package/src/ts/react-website/agui/generator.d.ts +1 -1
- package/src/utils/agent-core-packaging.d.ts +25 -12
- package/src/utils/agent-core-packaging.js +47 -11
- package/src/utils/agent-core-packaging.js.map +1 -1
- package/src/utils/docker.d.ts +1 -7
- package/src/utils/docker.js +1 -1
- package/src/utils/docker.js.map +1 -1
- package/src/utils/format.d.ts +5 -0
- package/src/utils/format.js +5 -0
- package/src/utils/format.js.map +1 -1
- package/src/utils/fs.d.ts +21 -11
- package/src/utils/fs.js +30 -22
- package/src/utils/fs.js.map +1 -1
- package/src/utils/version-lockstep/lockstep.d.ts +27 -0
- package/src/utils/version-lockstep/lockstep.js +46 -0
- package/src/utils/version-lockstep/lockstep.js.map +1 -0
- package/src/utils/versions.d.ts +9 -4
- package/src/utils/versions.js +24 -4
- package/src/utils/versions.js.map +1 -1
package/src/utils/docker.d.ts
CHANGED
|
@@ -6,13 +6,7 @@ import { type ProjectConfiguration, type Tree } from '@nx/devkit';
|
|
|
6
6
|
import { type DependencyDeclaration, type MustDeclare } from './declared-dependencies.js';
|
|
7
7
|
/** Dependencies a caller must declare to add a Docker scan target. */
|
|
8
8
|
export declare const DOCKER_DEPENDENCIES: readonly [{
|
|
9
|
-
readonly name: "
|
|
10
|
-
}, {
|
|
11
|
-
readonly name: "rimraf";
|
|
12
|
-
}, {
|
|
13
|
-
readonly name: "make-dir-cli";
|
|
14
|
-
}, {
|
|
15
|
-
readonly name: "cpy-cli";
|
|
9
|
+
readonly name: "shx";
|
|
16
10
|
}];
|
|
17
11
|
/**
|
|
18
12
|
* The `cache` setting every target that builds a container image must use.
|
package/src/utils/docker.js
CHANGED
|
@@ -95,7 +95,7 @@ const TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.
|
|
|
95
95
|
const commands = [
|
|
96
96
|
fs.rm(scanDir),
|
|
97
97
|
fs.mkdir(scanDir),
|
|
98
|
-
fs.
|
|
98
|
+
fs.cpFile(ignoreFilePath, joinPathFragments(scanDir, TRIVY_IGNORE_FILE))
|
|
99
99
|
];
|
|
100
100
|
imageTags.forEach((imageTag, index)=>{
|
|
101
101
|
const tarName = `image-${index}.tar`;
|
package/src/utils/docker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/docker.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type ProjectConfiguration,\n type Tree,\n} from '@nx/devkit';\nimport {\n type DependencyDeclaration,\n forDependencies,\n type MustDeclare,\n} from './declared-dependencies.js';\nimport { FS_DEPENDENCIES, FsCommands } from './fs.js';\nimport { addDependencyToTargetIfNotPresent } from './nx.js';\nimport { containerImage, type ITsDepVersion, TS_VERSIONS } from './versions.js';\n\n/** Dependencies a caller must declare to add a Docker scan target. */\nexport const DOCKER_DEPENDENCIES = [...FS_DEPENDENCIES] as const;\n\n/**\n * The `cache` setting every target that builds a container image must use.\n *\n * A built image lives in the container engine's own store, not under any\n * `outputs` path, so Nx has nothing to restore a cache hit from: it would\n * report the build complete while the image is missing, and the `trivy` scan\n * that depends on it would report a cached pass against an image that isn't\n * there. The container engine's layer cache is the sound cache here — it is\n * keyed on the build context and lives alongside the image, so an unchanged\n * context rebuilds in well under a second and always leaves the image present.\n */\nexport const IMAGE_BUILD_CACHE = false;\n\nconst TRIVY_IGNORE_FILE = '.trivyignore';\n\nconst TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.g. CVE-2021-12345) per line to\n# suppress it during the image scan (\\`nx run-many --target trivy\\`).\n# https://trivy.dev/latest/docs/configuration/filtering/#by-finding-ids\n`;\n\n/**\n * Substitution variables for the vended Node `Dockerfile` templates: the npm\n * version to install globally, and the versions its dependency overrides pin.\n */\nexport const nodeImageVersions = () => ({\n npmVersion: TS_VERSIONS.npm,\n minimatchVersion: TS_VERSIONS.minimatch,\n});\n\n/**\n * Packages a vended Node `Dockerfile` pins, which every generator writing one\n * must declare so the version sync keeps those pins current.\n *\n * Spread through `ownedElsewhere`: the pin lives in the image build rather than\n * in any manifest, so nothing is installed into the workspace. Left undeclared\n * these would be the only vended versions never upgraded — and they are\n * precisely the ones held at a version clear of a known HIGH/CRITICAL\n * vulnerability.\n */\nexport const NODE_IMAGE_DEPENDENCIES = [\n { name: 'npm' },\n { name: 'minimatch' },\n] as const satisfies readonly { name: ITsDepVersion }[];\n\n/**\n * Packages the AWS Distro for OpenTelemetry install in a vended `Dockerfile`\n * pins, for the images that auto-instrument with it. Spread through\n * `ownedElsewhere` for the same reason as {@link NODE_IMAGE_DEPENDENCIES}.\n */\nexport const ADOT_IMAGE_DEPENDENCIES = [\n { name: '@aws/aws-distro-opentelemetry-node-autoinstrumentation' },\n { name: '@opentelemetry/propagator-jaeger' },\n] as const satisfies readonly { name: ITsDepVersion }[];\n\nexport interface DockerScanTargetOptions {\n /**\n * Project configuration to add the scan target to (mutated in place).\n */\n readonly project: ProjectConfiguration;\n /**\n * Container engine command to invoke (\\`docker\\` or \\`finch\\`).\n */\n readonly containerEngine: string;\n /**\n * Name of the scan target to add, e.g. \\`my-agent-trivy\\`.\n */\n readonly trivyTargetName: string;\n /**\n * Name of the docker target that builds the image(s) to scan. The scan\n * target depends on it.\n */\n readonly dockerTargetName: string;\n /**\n * Tags of the images built by the docker target which should be scanned.\n */\n readonly imageTags: string[];\n}\n\n/**\n * Add a Trivy scan target for the images built by a docker target and wire it\n * under the aggregate \\`trivy\\` target. The scan is not part of \\`build\\` — run\n * \\`nx run-many --target trivy\\` (the root \\`trivy\\` script) in CI.\n *\n * The scan target depends on the docker target and is not cacheable, because\n * what it scans lives in the container engine rather than under its \\`outputs\\`.\n * Each image is saved to a tarball under \\`dist/<projectRoot>/trivy/<scan-key>\\`\n * (kept out of any Docker build context) and scanned with the pinned ECR-hosted\n * Trivy image via a workspace-relative bind mount, so the same commands work\n * under both docker and finch. The scan exits non-zero (exit code 1) on\n * HIGH/CRITICAL vulnerabilities.\n *\n * A \\`.trivyignore\\` is vended at the project root (kept if it already exists)\n * for suppressing findings.\n */\nexport const addDockerScanTarget = <const D extends DependencyDeclaration>(\n tree: Tree,\n options: DockerScanTargetOptions,\n declaration: D & MustDeclare<typeof DOCKER_DEPENDENCIES, D>,\n): void => {\n const { project, containerEngine, trivyTargetName, dockerTargetName } =\n options;\n const { imageTags } = options;\n const projectRoot = project.root;\n\n const ignoreFilePath = joinPathFragments(projectRoot, TRIVY_IGNORE_FILE);\n if (!tree.exists(ignoreFilePath)) {\n tree.write(ignoreFilePath, TRIVY_IGNORE_CONTENTS);\n }\n\n // Stage scan artifacts in a directory unique to this scan target so that\n // sibling targets in the same project (e.g. multiple agents) don't clobber\n // each other's tarballs when run in parallel. The first image tag is unique\n // per target, so it makes a stable, collision-free key.\n const scanKey = imageTags[0].replace(/[^a-zA-Z0-9-]/g, '-');\n const scanDir = joinPathFragments('dist', projectRoot, 'trivy', scanKey);\n const trivyImage = containerImage('trivy');\n\n const fs = new FsCommands(\n tree,\n forDependencies<typeof DOCKER_DEPENDENCIES>(declaration),\n );\n const commands = [\n fs.rm(scanDir),\n fs.mkdir(scanDir),\n fs.cp(ignoreFilePath, joinPathFragments(scanDir, TRIVY_IGNORE_FILE)),\n ];\n\n imageTags.forEach((imageTag, index) => {\n const tarName = `image-${index}.tar`;\n commands.push(\n `${containerEngine} save -o ${joinPathFragments(scanDir, tarName)} ${imageTag}`,\n `${containerEngine} run --rm -v \"./${scanDir}\":/scan ${trivyImage} image --input /scan/${tarName} --ignorefile /scan/${TRIVY_IGNORE_FILE} --scanners vuln --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 --no-progress -q`,\n );\n });\n\n project.targets ??= {};\n project.targets[trivyTargetName] = {\n // The image being scanned is only reachable through the container engine,\n // so a restored cache entry would report a pass for an image that may no\n // longer exist. The scan re-runs and either scans the real image or fails.\n // Nx only hashes a cacheable task, so declaring `inputs` here would have no\n // effect.\n cache: false,\n outputs: [`{workspaceRoot}/${scanDir}`],\n executor: 'nx:run-commands',\n options: {\n commands,\n parallel: false,\n },\n dependsOn: [dockerTargetName],\n };\n\n // Aggregate per-component scan targets under a single `trivy` target. The\n // scan is intentionally NOT wired into `build`: image scanning is slow and\n // its result depends on the ever-changing vulnerability database (a scan\n // that passes today can fail tomorrow when a new CVE is published), so\n // coupling it to `build` would make local and CI builds non-deterministic.\n // Run `nx run-many --target trivy` (the root `trivy` script) in CI instead.\n if (trivyTargetName !== 'trivy') {\n addDependencyToTargetIfNotPresent(project, 'trivy', trivyTargetName);\n }\n};\n"],"names":["joinPathFragments","forDependencies","FS_DEPENDENCIES","FsCommands","addDependencyToTargetIfNotPresent","containerImage","TS_VERSIONS","DOCKER_DEPENDENCIES","IMAGE_BUILD_CACHE","TRIVY_IGNORE_FILE","TRIVY_IGNORE_CONTENTS","nodeImageVersions","npmVersion","npm","minimatchVersion","minimatch","NODE_IMAGE_DEPENDENCIES","name","ADOT_IMAGE_DEPENDENCIES","addDockerScanTarget","tree","options","declaration","project","containerEngine","trivyTargetName","dockerTargetName","imageTags","projectRoot","root","ignoreFilePath","exists","write","scanKey","replace","scanDir","trivyImage","fs","commands","rm","mkdir","cp","forEach","imageTag","index","tarName","push","targets","cache","outputs","executor","parallel","dependsOn"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,QAGZ,aAAa;AACpB,SAEEC,eAAe,QAEV,6BAA6B;AACpC,SAASC,eAAe,EAAEC,UAAU,QAAQ,UAAU;AACtD,SAASC,iCAAiC,QAAQ,UAAU;AAC5D,SAASC,cAAc,EAAsBC,WAAW,QAAQ,gBAAgB;AAEhF,oEAAoE,GACpE,OAAO,MAAMC,sBAAsB;OAAIL;CAAgB,CAAU;AAEjE;;;;;;;;;;CAUC,GACD,OAAO,MAAMM,oBAAoB,MAAM;AAEvC,MAAMC,oBAAoB;AAE1B,MAAMC,wBAAwB,CAAC;;;AAG/B,CAAC;AAED;;;CAGC,GACD,OAAO,MAAMC,oBAAoB,IAAO,CAAA;QACtCC,YAAYN,YAAYO,GAAG;QAC3BC,kBAAkBR,YAAYS,SAAS;IACzC,CAAA,EAAG;AAEH;;;;;;;;;CASC,GACD,OAAO,MAAMC,0BAA0B;IACrC;QAAEC,MAAM;IAAM;IACd;QAAEA,MAAM;IAAY;CACrB,CAAuD;AAExD;;;;CAIC,GACD,OAAO,MAAMC,0BAA0B;IACrC;QAAED,MAAM;IAAyD;IACjE;QAAEA,MAAM;IAAmC;CAC5C,CAAuD;AA0BxD;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAME,sBAAsB,CACjCC,MACAC,SACAC;IAEA,MAAM,EAAEC,OAAO,EAAEC,eAAe,EAAEC,eAAe,EAAEC,gBAAgB,EAAE,GACnEL;IACF,MAAM,EAAEM,SAAS,EAAE,GAAGN;IACtB,MAAMO,cAAcL,QAAQM,IAAI;IAEhC,MAAMC,iBAAiB9B,kBAAkB4B,aAAanB;IACtD,IAAI,CAACW,KAAKW,MAAM,CAACD,iBAAiB;QAChCV,KAAKY,KAAK,CAACF,gBAAgBpB;IAC7B;IAEA,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,wDAAwD;IACxD,MAAMuB,UAAUN,SAAS,CAAC,EAAE,CAACO,OAAO,CAAC,kBAAkB;IACvD,MAAMC,UAAUnC,kBAAkB,QAAQ4B,aAAa,SAASK;IAChE,MAAMG,aAAa/B,eAAe;IAElC,MAAMgC,KAAK,IAAIlC,WACbiB,MACAnB,gBAA4CqB;IAE9C,MAAMgB,WAAW;QACfD,GAAGE,EAAE,CAACJ;QACNE,GAAGG,KAAK,CAACL;QACTE,GAAGI,EAAE,CAACX,gBAAgB9B,kBAAkBmC,SAAS1B;KAClD;IAEDkB,UAAUe,OAAO,CAAC,CAACC,UAAUC;QAC3B,MAAMC,UAAU,CAAC,MAAM,EAAED,MAAM,IAAI,CAAC;QACpCN,SAASQ,IAAI,CACX,GAAGtB,gBAAgB,SAAS,EAAExB,kBAAkBmC,SAASU,SAAS,CAAC,EAAEF,UAAU,EAC/E,GAAGnB,gBAAgB,gBAAgB,EAAEW,QAAQ,QAAQ,EAAEC,WAAW,qBAAqB,EAAES,QAAQ,oBAAoB,EAAEpC,kBAAkB,yFAAyF,CAAC;IAEvO;IAEAc,QAAQwB,OAAO,KAAK,CAAC;IACrBxB,QAAQwB,OAAO,CAACtB,gBAAgB,GAAG;QACjC,0EAA0E;QAC1E,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,UAAU;QACVuB,OAAO;QACPC,SAAS;YAAC,CAAC,gBAAgB,EAAEd,SAAS;SAAC;QACvCe,UAAU;QACV7B,SAAS;YACPiB;YACAa,UAAU;QACZ;QACAC,WAAW;YAAC1B;SAAiB;IAC/B;IAEA,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAID,oBAAoB,SAAS;QAC/BrB,kCAAkCmB,SAAS,SAASE;IACtD;AACF,EAAE"}
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/docker.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type ProjectConfiguration,\n type Tree,\n} from '@nx/devkit';\nimport {\n type DependencyDeclaration,\n forDependencies,\n type MustDeclare,\n} from './declared-dependencies.js';\nimport { FS_DEPENDENCIES, FsCommands } from './fs.js';\nimport { addDependencyToTargetIfNotPresent } from './nx.js';\nimport { containerImage, type ITsDepVersion, TS_VERSIONS } from './versions.js';\n\n/** Dependencies a caller must declare to add a Docker scan target. */\nexport const DOCKER_DEPENDENCIES = [...FS_DEPENDENCIES] as const;\n\n/**\n * The `cache` setting every target that builds a container image must use.\n *\n * A built image lives in the container engine's own store, not under any\n * `outputs` path, so Nx has nothing to restore a cache hit from: it would\n * report the build complete while the image is missing, and the `trivy` scan\n * that depends on it would report a cached pass against an image that isn't\n * there. The container engine's layer cache is the sound cache here — it is\n * keyed on the build context and lives alongside the image, so an unchanged\n * context rebuilds in well under a second and always leaves the image present.\n */\nexport const IMAGE_BUILD_CACHE = false;\n\nconst TRIVY_IGNORE_FILE = '.trivyignore';\n\nconst TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.g. CVE-2021-12345) per line to\n# suppress it during the image scan (\\`nx run-many --target trivy\\`).\n# https://trivy.dev/latest/docs/configuration/filtering/#by-finding-ids\n`;\n\n/**\n * Substitution variables for the vended Node `Dockerfile` templates: the npm\n * version to install globally, and the versions its dependency overrides pin.\n */\nexport const nodeImageVersions = () => ({\n npmVersion: TS_VERSIONS.npm,\n minimatchVersion: TS_VERSIONS.minimatch,\n});\n\n/**\n * Packages a vended Node `Dockerfile` pins, which every generator writing one\n * must declare so the version sync keeps those pins current.\n *\n * Spread through `ownedElsewhere`: the pin lives in the image build rather than\n * in any manifest, so nothing is installed into the workspace. Left undeclared\n * these would be the only vended versions never upgraded — and they are\n * precisely the ones held at a version clear of a known HIGH/CRITICAL\n * vulnerability.\n */\nexport const NODE_IMAGE_DEPENDENCIES = [\n { name: 'npm' },\n { name: 'minimatch' },\n] as const satisfies readonly { name: ITsDepVersion }[];\n\n/**\n * Packages the AWS Distro for OpenTelemetry install in a vended `Dockerfile`\n * pins, for the images that auto-instrument with it. Spread through\n * `ownedElsewhere` for the same reason as {@link NODE_IMAGE_DEPENDENCIES}.\n */\nexport const ADOT_IMAGE_DEPENDENCIES = [\n { name: '@aws/aws-distro-opentelemetry-node-autoinstrumentation' },\n { name: '@opentelemetry/propagator-jaeger' },\n] as const satisfies readonly { name: ITsDepVersion }[];\n\nexport interface DockerScanTargetOptions {\n /**\n * Project configuration to add the scan target to (mutated in place).\n */\n readonly project: ProjectConfiguration;\n /**\n * Container engine command to invoke (\\`docker\\` or \\`finch\\`).\n */\n readonly containerEngine: string;\n /**\n * Name of the scan target to add, e.g. \\`my-agent-trivy\\`.\n */\n readonly trivyTargetName: string;\n /**\n * Name of the docker target that builds the image(s) to scan. The scan\n * target depends on it.\n */\n readonly dockerTargetName: string;\n /**\n * Tags of the images built by the docker target which should be scanned.\n */\n readonly imageTags: string[];\n}\n\n/**\n * Add a Trivy scan target for the images built by a docker target and wire it\n * under the aggregate \\`trivy\\` target. The scan is not part of \\`build\\` — run\n * \\`nx run-many --target trivy\\` (the root \\`trivy\\` script) in CI.\n *\n * The scan target depends on the docker target and is not cacheable, because\n * what it scans lives in the container engine rather than under its \\`outputs\\`.\n * Each image is saved to a tarball under \\`dist/<projectRoot>/trivy/<scan-key>\\`\n * (kept out of any Docker build context) and scanned with the pinned ECR-hosted\n * Trivy image via a workspace-relative bind mount, so the same commands work\n * under both docker and finch. The scan exits non-zero (exit code 1) on\n * HIGH/CRITICAL vulnerabilities.\n *\n * A \\`.trivyignore\\` is vended at the project root (kept if it already exists)\n * for suppressing findings.\n */\nexport const addDockerScanTarget = <const D extends DependencyDeclaration>(\n tree: Tree,\n options: DockerScanTargetOptions,\n declaration: D & MustDeclare<typeof DOCKER_DEPENDENCIES, D>,\n): void => {\n const { project, containerEngine, trivyTargetName, dockerTargetName } =\n options;\n const { imageTags } = options;\n const projectRoot = project.root;\n\n const ignoreFilePath = joinPathFragments(projectRoot, TRIVY_IGNORE_FILE);\n if (!tree.exists(ignoreFilePath)) {\n tree.write(ignoreFilePath, TRIVY_IGNORE_CONTENTS);\n }\n\n // Stage scan artifacts in a directory unique to this scan target so that\n // sibling targets in the same project (e.g. multiple agents) don't clobber\n // each other's tarballs when run in parallel. The first image tag is unique\n // per target, so it makes a stable, collision-free key.\n const scanKey = imageTags[0].replace(/[^a-zA-Z0-9-]/g, '-');\n const scanDir = joinPathFragments('dist', projectRoot, 'trivy', scanKey);\n const trivyImage = containerImage('trivy');\n\n const fs = new FsCommands(\n tree,\n forDependencies<typeof DOCKER_DEPENDENCIES>(declaration),\n );\n const commands = [\n fs.rm(scanDir),\n fs.mkdir(scanDir),\n fs.cpFile(ignoreFilePath, joinPathFragments(scanDir, TRIVY_IGNORE_FILE)),\n ];\n\n imageTags.forEach((imageTag, index) => {\n const tarName = `image-${index}.tar`;\n commands.push(\n `${containerEngine} save -o ${joinPathFragments(scanDir, tarName)} ${imageTag}`,\n `${containerEngine} run --rm -v \"./${scanDir}\":/scan ${trivyImage} image --input /scan/${tarName} --ignorefile /scan/${TRIVY_IGNORE_FILE} --scanners vuln --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 --no-progress -q`,\n );\n });\n\n project.targets ??= {};\n project.targets[trivyTargetName] = {\n // The image being scanned is only reachable through the container engine,\n // so a restored cache entry would report a pass for an image that may no\n // longer exist. The scan re-runs and either scans the real image or fails.\n // Nx only hashes a cacheable task, so declaring `inputs` here would have no\n // effect.\n cache: false,\n outputs: [`{workspaceRoot}/${scanDir}`],\n executor: 'nx:run-commands',\n options: {\n commands,\n parallel: false,\n },\n dependsOn: [dockerTargetName],\n };\n\n // Aggregate per-component scan targets under a single `trivy` target. The\n // scan is intentionally NOT wired into `build`: image scanning is slow and\n // its result depends on the ever-changing vulnerability database (a scan\n // that passes today can fail tomorrow when a new CVE is published), so\n // coupling it to `build` would make local and CI builds non-deterministic.\n // Run `nx run-many --target trivy` (the root `trivy` script) in CI instead.\n if (trivyTargetName !== 'trivy') {\n addDependencyToTargetIfNotPresent(project, 'trivy', trivyTargetName);\n }\n};\n"],"names":["joinPathFragments","forDependencies","FS_DEPENDENCIES","FsCommands","addDependencyToTargetIfNotPresent","containerImage","TS_VERSIONS","DOCKER_DEPENDENCIES","IMAGE_BUILD_CACHE","TRIVY_IGNORE_FILE","TRIVY_IGNORE_CONTENTS","nodeImageVersions","npmVersion","npm","minimatchVersion","minimatch","NODE_IMAGE_DEPENDENCIES","name","ADOT_IMAGE_DEPENDENCIES","addDockerScanTarget","tree","options","declaration","project","containerEngine","trivyTargetName","dockerTargetName","imageTags","projectRoot","root","ignoreFilePath","exists","write","scanKey","replace","scanDir","trivyImage","fs","commands","rm","mkdir","cpFile","forEach","imageTag","index","tarName","push","targets","cache","outputs","executor","parallel","dependsOn"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,QAGZ,aAAa;AACpB,SAEEC,eAAe,QAEV,6BAA6B;AACpC,SAASC,eAAe,EAAEC,UAAU,QAAQ,UAAU;AACtD,SAASC,iCAAiC,QAAQ,UAAU;AAC5D,SAASC,cAAc,EAAsBC,WAAW,QAAQ,gBAAgB;AAEhF,oEAAoE,GACpE,OAAO,MAAMC,sBAAsB;OAAIL;CAAgB,CAAU;AAEjE;;;;;;;;;;CAUC,GACD,OAAO,MAAMM,oBAAoB,MAAM;AAEvC,MAAMC,oBAAoB;AAE1B,MAAMC,wBAAwB,CAAC;;;AAG/B,CAAC;AAED;;;CAGC,GACD,OAAO,MAAMC,oBAAoB,IAAO,CAAA;QACtCC,YAAYN,YAAYO,GAAG;QAC3BC,kBAAkBR,YAAYS,SAAS;IACzC,CAAA,EAAG;AAEH;;;;;;;;;CASC,GACD,OAAO,MAAMC,0BAA0B;IACrC;QAAEC,MAAM;IAAM;IACd;QAAEA,MAAM;IAAY;CACrB,CAAuD;AAExD;;;;CAIC,GACD,OAAO,MAAMC,0BAA0B;IACrC;QAAED,MAAM;IAAyD;IACjE;QAAEA,MAAM;IAAmC;CAC5C,CAAuD;AA0BxD;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAME,sBAAsB,CACjCC,MACAC,SACAC;IAEA,MAAM,EAAEC,OAAO,EAAEC,eAAe,EAAEC,eAAe,EAAEC,gBAAgB,EAAE,GACnEL;IACF,MAAM,EAAEM,SAAS,EAAE,GAAGN;IACtB,MAAMO,cAAcL,QAAQM,IAAI;IAEhC,MAAMC,iBAAiB9B,kBAAkB4B,aAAanB;IACtD,IAAI,CAACW,KAAKW,MAAM,CAACD,iBAAiB;QAChCV,KAAKY,KAAK,CAACF,gBAAgBpB;IAC7B;IAEA,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,wDAAwD;IACxD,MAAMuB,UAAUN,SAAS,CAAC,EAAE,CAACO,OAAO,CAAC,kBAAkB;IACvD,MAAMC,UAAUnC,kBAAkB,QAAQ4B,aAAa,SAASK;IAChE,MAAMG,aAAa/B,eAAe;IAElC,MAAMgC,KAAK,IAAIlC,WACbiB,MACAnB,gBAA4CqB;IAE9C,MAAMgB,WAAW;QACfD,GAAGE,EAAE,CAACJ;QACNE,GAAGG,KAAK,CAACL;QACTE,GAAGI,MAAM,CAACX,gBAAgB9B,kBAAkBmC,SAAS1B;KACtD;IAEDkB,UAAUe,OAAO,CAAC,CAACC,UAAUC;QAC3B,MAAMC,UAAU,CAAC,MAAM,EAAED,MAAM,IAAI,CAAC;QACpCN,SAASQ,IAAI,CACX,GAAGtB,gBAAgB,SAAS,EAAExB,kBAAkBmC,SAASU,SAAS,CAAC,EAAEF,UAAU,EAC/E,GAAGnB,gBAAgB,gBAAgB,EAAEW,QAAQ,QAAQ,EAAEC,WAAW,qBAAqB,EAAES,QAAQ,oBAAoB,EAAEpC,kBAAkB,yFAAyF,CAAC;IAEvO;IAEAc,QAAQwB,OAAO,KAAK,CAAC;IACrBxB,QAAQwB,OAAO,CAACtB,gBAAgB,GAAG;QACjC,0EAA0E;QAC1E,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,UAAU;QACVuB,OAAO;QACPC,SAAS;YAAC,CAAC,gBAAgB,EAAEd,SAAS;SAAC;QACvCe,UAAU;QACV7B,SAAS;YACPiB;YACAa,UAAU;QACZ;QACAC,WAAW;YAAC1B;SAAiB;IAC/B;IAEA,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAID,oBAAoB,SAAS;QAC/BrB,kCAAkCmB,SAAS,SAASE;IACtD;AACF,EAAE"}
|
package/src/utils/format.d.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { type Tree } from '@nx/devkit';
|
|
6
|
+
/**
|
|
7
|
+
* The Biome release the wasm bindings are built from. Must match
|
|
8
|
+
* `TS_VERSIONS['@biomejs/biome']`, the CLI the vended targets run.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BIOME_WASM_VERSION: string;
|
|
6
11
|
/**
|
|
7
12
|
* Excludes test reports — including the vendored scripts vitest's coverage HTML
|
|
8
13
|
* reporter writes — from formatting and linting.
|
package/src/utils/format.js
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/ import { Biome } from "@biomejs/js-api/nodejs";
|
|
5
5
|
import { getProjects } from "@nx/devkit";
|
|
6
|
+
import { createRequire } from "module";
|
|
6
7
|
import path from "path";
|
|
7
8
|
import { ruffFixAndFormat } from "./ruff.js";
|
|
8
9
|
import { tryReadToml } from "./toml.js";
|
|
9
10
|
import { TS_VERSIONS } from "./versions.js";
|
|
11
|
+
/**
|
|
12
|
+
* The Biome release the wasm bindings are built from. Must match
|
|
13
|
+
* `TS_VERSIONS['@biomejs/biome']`, the CLI the vended targets run.
|
|
14
|
+
*/ export const BIOME_WASM_VERSION = createRequire(import.meta.url)('@biomejs/wasm-nodejs/package.json').version;
|
|
10
15
|
/**
|
|
11
16
|
* Excludes test reports — including the vendored scripts vitest's coverage HTML
|
|
12
17
|
* reporter writes — from formatting and linting.
|
package/src/utils/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/format.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Biome } from '@biomejs/js-api/nodejs';\nimport { getProjects, type Tree } from '@nx/devkit';\nimport path from 'path';\nimport { type RuffOptions, ruffFixAndFormat } from './ruff.js';\nimport { tryReadToml } from './toml.js';\nimport { TS_VERSIONS } from './versions.js';\n\n/**\n * Excludes test reports — including the vendored scripts vitest's coverage HTML\n * reporter writes — from formatting and linting.\n */\nexport const BIOME_TEST_OUTPUT_EXCLUDE = '!**/test-output';\n\n/**\n * The biome.json vended into a new workspace. The pnpm catalog resolver is only\n * included on pnpm workspaces, since `experimentalPnpmCatalogs` is Biome's only\n * catalog resolver and reads `pnpm-workspace.yaml` exclusively — it does nothing\n * for yarn or bun catalogs, so vending it there would be misleading.\n */\nexport const getDefaultBiomeConfig = (tree: Tree) => ({\n $schema: `https://biomejs.dev/schemas/${TS_VERSIONS['@biomejs/biome']}/schema.json`,\n root: true,\n formatter: {\n enabled: true,\n indentStyle: 'space',\n indentWidth: 2,\n lineWidth: 80,\n },\n javascript: {\n formatter: {\n quoteStyle: 'single',\n trailingCommas: 'all',\n },\n // Resolve `catalog:` versions from pnpm-workspace.yaml (pnpm workspaces only).\n ...(tree.exists('pnpm-workspace.yaml')\n ? { resolver: { experimentalPnpmCatalogs: true } }\n : {}),\n },\n css: {\n formatter: {\n quoteStyle: 'single',\n },\n linter: {\n enabled: false,\n },\n },\n linter: {\n enabled: true,\n rules: {\n preset: 'none',\n correctness: {\n // Every project must declare the third-party dependencies its source\n // code imports in its own package.json.\n noUndeclaredDependencies: 'error',\n },\n },\n },\n assist: {\n actions: {\n source: {\n organizeImports: 'on',\n },\n },\n },\n files: {\n includes: [\n '**',\n '!**/dist',\n '!**/out-tsc',\n BIOME_TEST_OUTPUT_EXCLUDE,\n '!**/node_modules',\n '!**/.nx',\n '!**/.venv',\n // GritQL codemod cache written by generators — its sample sources\n // otherwise pollute a bare `biome check .` with parse errors.\n '!**/.grit',\n '!**/*.css',\n '!**/*.gen.*',\n '!**/generated/**',\n '!**/tsconfig*.json',\n ],\n },\n // Config files, build scripts and tests use root tooling rather than\n // declaring it per-project, so the undeclared-dependency rule is off for them.\n overrides: [\n {\n includes: [\n '**/*.config.{ts,mts,cts,js,mjs,cjs}',\n '**/*.{spec,test}.{ts,tsx,mts,cts,js,jsx,mjs,cjs}',\n '**/*.stories.{ts,tsx}',\n ],\n linter: {\n rules: {\n correctness: {\n noUndeclaredDependencies: 'off',\n },\n },\n },\n },\n ],\n});\n\nconst BIOME_FORMATTABLE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.mts',\n '.cts',\n '.json',\n '.jsonc',\n '.css',\n]);\n\n/** Matches `tsconfig.json` and variants like `tsconfig.lib.json`. */\nconst isTsConfig = (filePath: string): boolean =>\n /(^|\\/)tsconfig[^/]*\\.json$/.test(filePath);\n\nexport interface FormatFilesInSubtreeOptions {\n /**\n * Paths, relative to the workspace root, to leave untouched.\n *\n * For files another tool owns the formatting of: formatting them here makes\n * generation non-idempotent, since the tool rewrites them in its own shape on\n * the next run and the workspace flips between the two forms.\n */\n readonly ignore?: readonly string[];\n}\n\n/**\n * Paths declared ignored for a tree, which stay ignored for every later call.\n *\n * A call formats every change pending in the tree, not only the ones its caller\n * made, so the list cannot be scoped to the call that passes it: generators\n * composing on one tree would reformat a file an earlier generator had excluded.\n * Once the generator that owns a file declares it ignored, it stays ignored for\n * the rest of the run.\n */\nconst treeIgnoredPaths = new WeakMap<Tree, Set<string>>();\n\n/**\n * Tree paths are workspace-relative and forward-slash separated. Normalise so a\n * caller building a path with `path.join` on Windows still matches.\n */\nconst normalizeTreePath = (filePath: string): string =>\n filePath.split(path.sep).join('/').replace(/^\\.\\//, '');\n\n/**\n * Format files in the given directory within the tree.\n * Handles both TypeScript/JavaScript/JSON (via biome) and Python (via ruff) files.\n * See https://github.com/nrwl/nx/blob/4cd640a9187954505d12de5b6d76a90d8ce4c2eb/packages/devkit/src/generators/format-files.ts#L11\n */\nexport async function formatFilesInSubtree(\n tree: Tree,\n dir?: string,\n options?: FormatFilesInSubtreeOptions,\n): Promise<void> {\n let ignored = treeIgnoredPaths.get(tree);\n if (options?.ignore?.length) {\n if (!ignored) {\n ignored = new Set();\n treeIgnoredPaths.set(tree, ignored);\n }\n for (const filePath of options.ignore) {\n ignored.add(normalizeTreePath(filePath));\n }\n }\n const changedFiles = tree\n .listChanges()\n .filter((file) => file.type !== 'DELETE')\n .filter((file) => (dir ? file.path.startsWith(dir) : true))\n .filter((file) => !ignored?.has(normalizeTreePath(file.path)));\n\n const pyFiles = changedFiles.filter((file) => file.path.endsWith('.py'));\n const otherFiles = changedFiles.filter(\n (file) =>\n BIOME_FORMATTABLE_EXTENSIONS.has(path.extname(file.path)) &&\n // tsconfigs are not biome-managed: they're excluded from the vended\n // format target (Nx's typescript-sync rewrites them without formatting),\n // so formatting them at generation would only diverge from the form\n // written on later runs. Leave them as updateJson/writeJson emit them so\n // repeated generation stays idempotent.\n !isTsConfig(file.path),\n );\n\n // Resolve each project's ruff settings (module names, line-length) so files\n // are formatted to match the on-disk build (see getPythonProjectRuffConfigs).\n const pythonProjectConfigs = pyFiles.length\n ? getPythonProjectRuffConfigs(tree)\n : [];\n\n // Format Python files with ruff (lint fixes + formatting)\n for (const file of pyFiles) {\n try {\n const content = ruffFixAndFormat(\n file.content.toString('utf-8'),\n file.path,\n resolveRuffOptions(\n readRuffConfig(tree, file.path),\n getOwningProjectRuffConfig(file.path, pythonProjectConfigs),\n ),\n );\n tree.write(file.path, content);\n } catch {\n // Silently skip ruff formatting failures\n }\n }\n\n if (otherFiles.length === 0) return;\n\n formatWithBiome(tree, otherFiles);\n}\n\n/**\n * The Biome configuration to format with: the workspace's own `biome.json`, else\n * the config we vend.\n *\n * Read through the tree, which falls back to disk for a file it doesn't hold. So\n * this resolves the most current config either way — the workspace's on-disk one,\n * or the version a generator has just written to the tree and not yet flushed,\n * which shelling out to the CLI could never see.\n */\nfunction readBiomeConfig(tree: Tree): unknown {\n const config = tree.read('biome.json', 'utf-8');\n if (config) {\n try {\n return JSON.parse(config);\n } catch {\n // Malformed config — fall through to the config we vend\n }\n }\n return getDefaultBiomeConfig(tree);\n}\n\n/**\n * Format files with Biome in-process, reusing one instance across the batch.\n *\n * Replaces one `biome format --stdin-file-path` process per file, which\n * dominated generation at ~70ms each: `formatFilesInSubtree` formats every\n * change accumulated in the tree, not only the ones its caller made, so\n * generators sharing a tree reformat the same files once per call. In-process is\n * ~0.5ms per file. Output was verified byte-identical across the plugin's whole\n * source tree, except that the CLI corrupts control characters passed through\n * stdin (a NUL in a template literal) where formatting in-process preserves them.\n */\nfunction formatWithBiome(\n tree: Tree,\n files: { path: string; content: Buffer | null }[],\n): void {\n try {\n const biome = new Biome();\n const { projectKey } = biome.openProject();\n biome.applyConfiguration(projectKey, readBiomeConfig(tree));\n\n for (const file of files) {\n try {\n const { content } = biome.formatContent(\n projectKey,\n file.content?.toString('utf-8') ?? '',\n { filePath: file.path },\n );\n tree.write(file.path, content);\n } catch {\n // Leave individual files that fail to format untouched\n }\n }\n } catch {\n // Silently skip formatting failures\n }\n}\n\n/**\n * Read the ruff config for a file, by walking from its directory up to the\n * workspace root looking for `.ruff.toml`, `ruff.toml`, or a `pyproject.toml`\n * with a `[tool.ruff]` section — the same files, in the same order, that ruff\n * itself resolves. The walk stops at the workspace root so a stray config in a\n * parent of the workspace (or the home directory) is never treated as the\n * project's.\n *\n * The settings are read rather than merely detected because formatting runs\n * in-process against tree content, so ruff never sees the file's location and\n * cannot resolve the config itself (see {@link resolveRuffOptions}).\n *\n * Reads go through the tree, which falls through to disk for files this run has\n * not touched, so a config the generator has just written in memory is picked up\n * as well as one already on disk.\n */\nfunction readRuffConfig(tree: Tree, filePath: string): RuffOptions | undefined {\n let dir = path.dirname(filePath);\n while (true) {\n for (const name of ['.ruff.toml', 'ruff.toml']) {\n const config = tryReadToml(tree, path.join(dir, name));\n if (config) {\n return config as RuffOptions;\n }\n }\n const ruff = (tryReadToml(tree, path.join(dir, 'pyproject.toml')) as any)\n ?.tool?.ruff;\n if (ruff) {\n return ruff as RuffOptions;\n }\n // Stop once the workspace root ('.') has been checked.\n if (dir === '.' || dir === '' || dir === path.dirname(dir)) {\n return undefined;\n }\n dir = path.dirname(dir);\n }\n}\n\n/**\n * The `[tool.ruff.lint].select` generated Python projects vend. Generation\n * formats before that config lands on disk, so it is pinned here to keep\n * generated files clean under the project's own `lint` target rather than under\n * whatever ruff's defaults happen to be for the pinned release.\n */\nconst DEFAULT_RUFF_SELECT = ['E', 'F', 'UP', 'B', 'SIM', 'I'];\n\ninterface PythonProjectRuffConfig {\n /** Project root, normalised to use forward slashes. */\n readonly root: string;\n /** Top-level importable module names declared by the project. */\n readonly modules: string[];\n /** The project's `[tool.ruff].line-length`, if set. */\n readonly lineLength?: number;\n /**\n * Ruff `target-version` (eg `py314`) derived from `[project].requires-python`.\n * Generation formats via stdin with no pyproject, so it must be passed\n * explicitly — ruff's formatting differs by target.\n */\n readonly targetVersion?: string;\n /** The project's `[tool.ruff.lint].select`, if set. */\n readonly select?: string[];\n}\n\n/**\n * Derive ruff's `target-version` (eg `py314`) from a PEP 508\n * `requires-python` specifier (eg `>=3.14`). Ruff targets the minimum\n * supported version, so take the lowest `major.minor` mentioned.\n */\nexport const requiresPythonToRuffTarget = (\n requiresPython: unknown,\n): string | undefined => {\n if (typeof requiresPython !== 'string') {\n return undefined;\n }\n let min: { major: number; minor: number } | undefined;\n for (const match of requiresPython.matchAll(/(\\d+)\\.(\\d+)/g)) {\n const major = Number(match[1]);\n const minor = Number(match[2]);\n if (\n !min ||\n major < min.major ||\n (major === min.major && minor < min.minor)\n ) {\n min = { major, minor };\n }\n }\n return min ? `py${min.major}${min.minor}` : undefined;\n};\n\n/**\n * Map each Nx project with a `pyproject.toml` to the ruff settings the on-disk\n * build enforces for it: its top-level module names (from\n * `[tool.hatch.build.targets.wheel].packages`), its `[tool.ruff].line-length`\n * and its `[tool.ruff.lint].select`.\n */\nfunction getPythonProjectRuffConfigs(tree: Tree): PythonProjectRuffConfig[] {\n const configs: PythonProjectRuffConfig[] = [];\n\n for (const project of getProjects(tree).values()) {\n // Projects without a pyproject.toml, or whose one cannot be parsed, have no\n // ruff settings to contribute.\n const pyproject = tryReadToml(\n tree,\n path.join(project.root, 'pyproject.toml'),\n ) as any;\n if (!pyproject) {\n continue;\n }\n const wheelPackages: unknown =\n pyproject?.tool?.hatch?.build?.targets?.wheel?.packages;\n // Record the top-level module segment (`pkg/sub` -> `pkg`), which is\n // all `known-first-party` keys off.\n const modules = Array.isArray(wheelPackages)\n ? wheelPackages\n .filter((pkg): pkg is string => typeof pkg === 'string' && !!pkg)\n .map((pkg) => pkg.split('/')[0])\n : [];\n const lineLength: unknown = pyproject?.tool?.ruff?.['line-length'];\n const targetVersion = requiresPythonToRuffTarget(\n pyproject?.project?.['requires-python'],\n );\n const selected: unknown = pyproject?.tool?.ruff?.lint?.select;\n const select = Array.isArray(selected)\n ? selected.filter(\n (rule): rule is string => typeof rule === 'string' && !!rule,\n )\n : undefined;\n if (\n modules.length ||\n typeof lineLength === 'number' ||\n targetVersion ||\n select?.length\n ) {\n configs.push({\n root: project.root.split(path.sep).join('/'),\n modules,\n lineLength: typeof lineLength === 'number' ? lineLength : undefined,\n targetVersion,\n select: select?.length ? select : undefined,\n });\n }\n }\n\n return configs;\n}\n\n/**\n * Resolve the ruff config for the project that owns a file (the project with\n * the longest root that is a prefix of the file path). Ruff runs per-project on\n * disk, so a file's settings come from its own project — only its own module is\n * first-party (sibling workspace packages are third-party) and its own\n * line-length applies — and scoping this way keeps in-tree formatting\n * consistent with the on-disk build.\n */\nfunction getOwningProjectRuffConfig(\n filePath: string,\n configs: PythonProjectRuffConfig[],\n): PythonProjectRuffConfig | undefined {\n let owner: PythonProjectRuffConfig | undefined;\n for (const config of configs) {\n if (\n (filePath === config.root || filePath.startsWith(`${config.root}/`)) &&\n (!owner || config.root.length > owner.root.length)\n ) {\n owner = config;\n }\n }\n return owner;\n}\n\n/**\n * Resolve the ruff settings to format a Python file with, mirroring what the\n * file's build enforces.\n *\n * Ruff's own config discovery never runs: settings are passed to the linter\n * directly rather than resolved from the file's location, which it never sees.\n * That also means a stray config above the workspace (or in the home directory)\n * can never be picked up.\n *\n * `config` is the file's nearest ruff config, whose rule selection is honoured\n * as-is. Without one, ruff would fall back to its own defaults, which change\n * between releases — 0.16 widened them from `E` and `F` to 36 rule prefixes — so\n * generation would apply fixes the project's build never asks for (eg `RUF022`\n * reordering `__all__`). Instead the project's own `lint.select` is pinned, so\n * generation enforces exactly what `lint` does and nothing more.\n *\n * `projectConfig` layers on the settings derived from the owning project rather\n * than declared under `[tool.ruff]`: `known-first-party` (the project's own\n * modules, from its wheel packages) keeps its imports in their own group, and\n * `target-version` (from its `requires-python`) matches the formatting the build\n * produces.\n */\nfunction resolveRuffOptions(\n config: RuffOptions | undefined,\n projectConfig?: PythonProjectRuffConfig,\n): RuffOptions {\n const lint: Record<string, unknown> = { ...config?.lint };\n // Pin the rule selection only when deferring to ruff's defaults would\n // otherwise apply rules the project's build does not enforce.\n if (!config) {\n lint.select = projectConfig?.select ?? DEFAULT_RUFF_SELECT;\n }\n if (projectConfig?.modules.length) {\n lint.isort = {\n ...(lint.isort as object | undefined),\n 'known-first-party': projectConfig.modules,\n };\n }\n return {\n ...config,\n ...(typeof projectConfig?.lineLength === 'number'\n ? { 'line-length': projectConfig.lineLength }\n : {}),\n ...(projectConfig?.targetVersion\n ? { 'target-version': projectConfig.targetVersion }\n : {}),\n lint,\n };\n}\n"],"names":["Biome","getProjects","path","ruffFixAndFormat","tryReadToml","TS_VERSIONS","BIOME_TEST_OUTPUT_EXCLUDE","getDefaultBiomeConfig","tree","$schema","root","formatter","enabled","indentStyle","indentWidth","lineWidth","javascript","quoteStyle","trailingCommas","exists","resolver","experimentalPnpmCatalogs","css","linter","rules","preset","correctness","noUndeclaredDependencies","assist","actions","source","organizeImports","files","includes","overrides","BIOME_FORMATTABLE_EXTENSIONS","Set","isTsConfig","filePath","test","treeIgnoredPaths","WeakMap","normalizeTreePath","split","sep","join","replace","formatFilesInSubtree","dir","options","ignored","get","ignore","length","set","add","changedFiles","listChanges","filter","file","type","startsWith","has","pyFiles","endsWith","otherFiles","extname","pythonProjectConfigs","getPythonProjectRuffConfigs","content","toString","resolveRuffOptions","readRuffConfig","getOwningProjectRuffConfig","write","formatWithBiome","readBiomeConfig","config","read","JSON","parse","biome","projectKey","openProject","applyConfiguration","formatContent","dirname","name","ruff","tool","undefined","DEFAULT_RUFF_SELECT","requiresPythonToRuffTarget","requiresPython","min","match","matchAll","major","Number","minor","configs","project","values","pyproject","wheelPackages","hatch","build","targets","wheel","packages","modules","Array","isArray","pkg","map","lineLength","targetVersion","selected","lint","select","rule","push","owner","projectConfig","isort"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/format.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Biome } from '@biomejs/js-api/nodejs';\nimport { getProjects, type Tree } from '@nx/devkit';\nimport { createRequire } from 'module';\nimport path from 'path';\nimport { type RuffOptions, ruffFixAndFormat } from './ruff.js';\nimport { tryReadToml } from './toml.js';\nimport { TS_VERSIONS } from './versions.js';\n\n/**\n * The Biome release the wasm bindings are built from. Must match\n * `TS_VERSIONS['@biomejs/biome']`, the CLI the vended targets run.\n */\nexport const BIOME_WASM_VERSION: string = createRequire(import.meta.url)(\n '@biomejs/wasm-nodejs/package.json',\n).version;\n\n/**\n * Excludes test reports — including the vendored scripts vitest's coverage HTML\n * reporter writes — from formatting and linting.\n */\nexport const BIOME_TEST_OUTPUT_EXCLUDE = '!**/test-output';\n\n/**\n * The biome.json vended into a new workspace. The pnpm catalog resolver is only\n * included on pnpm workspaces, since `experimentalPnpmCatalogs` is Biome's only\n * catalog resolver and reads `pnpm-workspace.yaml` exclusively — it does nothing\n * for yarn or bun catalogs, so vending it there would be misleading.\n */\nexport const getDefaultBiomeConfig = (tree: Tree) => ({\n $schema: `https://biomejs.dev/schemas/${TS_VERSIONS['@biomejs/biome']}/schema.json`,\n root: true,\n formatter: {\n enabled: true,\n indentStyle: 'space',\n indentWidth: 2,\n lineWidth: 80,\n },\n javascript: {\n formatter: {\n quoteStyle: 'single',\n trailingCommas: 'all',\n },\n // Resolve `catalog:` versions from pnpm-workspace.yaml (pnpm workspaces only).\n ...(tree.exists('pnpm-workspace.yaml')\n ? { resolver: { experimentalPnpmCatalogs: true } }\n : {}),\n },\n css: {\n formatter: {\n quoteStyle: 'single',\n },\n linter: {\n enabled: false,\n },\n },\n linter: {\n enabled: true,\n rules: {\n preset: 'none',\n correctness: {\n // Every project must declare the third-party dependencies its source\n // code imports in its own package.json.\n noUndeclaredDependencies: 'error',\n },\n },\n },\n assist: {\n actions: {\n source: {\n organizeImports: 'on',\n },\n },\n },\n files: {\n includes: [\n '**',\n '!**/dist',\n '!**/out-tsc',\n BIOME_TEST_OUTPUT_EXCLUDE,\n '!**/node_modules',\n '!**/.nx',\n '!**/.venv',\n // GritQL codemod cache written by generators — its sample sources\n // otherwise pollute a bare `biome check .` with parse errors.\n '!**/.grit',\n '!**/*.css',\n '!**/*.gen.*',\n '!**/generated/**',\n '!**/tsconfig*.json',\n ],\n },\n // Config files, build scripts and tests use root tooling rather than\n // declaring it per-project, so the undeclared-dependency rule is off for them.\n overrides: [\n {\n includes: [\n '**/*.config.{ts,mts,cts,js,mjs,cjs}',\n '**/*.{spec,test}.{ts,tsx,mts,cts,js,jsx,mjs,cjs}',\n '**/*.stories.{ts,tsx}',\n ],\n linter: {\n rules: {\n correctness: {\n noUndeclaredDependencies: 'off',\n },\n },\n },\n },\n ],\n});\n\nconst BIOME_FORMATTABLE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.mts',\n '.cts',\n '.json',\n '.jsonc',\n '.css',\n]);\n\n/** Matches `tsconfig.json` and variants like `tsconfig.lib.json`. */\nconst isTsConfig = (filePath: string): boolean =>\n /(^|\\/)tsconfig[^/]*\\.json$/.test(filePath);\n\nexport interface FormatFilesInSubtreeOptions {\n /**\n * Paths, relative to the workspace root, to leave untouched.\n *\n * For files another tool owns the formatting of: formatting them here makes\n * generation non-idempotent, since the tool rewrites them in its own shape on\n * the next run and the workspace flips between the two forms.\n */\n readonly ignore?: readonly string[];\n}\n\n/**\n * Paths declared ignored for a tree, which stay ignored for every later call.\n *\n * A call formats every change pending in the tree, not only the ones its caller\n * made, so the list cannot be scoped to the call that passes it: generators\n * composing on one tree would reformat a file an earlier generator had excluded.\n * Once the generator that owns a file declares it ignored, it stays ignored for\n * the rest of the run.\n */\nconst treeIgnoredPaths = new WeakMap<Tree, Set<string>>();\n\n/**\n * Tree paths are workspace-relative and forward-slash separated. Normalise so a\n * caller building a path with `path.join` on Windows still matches.\n */\nconst normalizeTreePath = (filePath: string): string =>\n filePath.split(path.sep).join('/').replace(/^\\.\\//, '');\n\n/**\n * Format files in the given directory within the tree.\n * Handles both TypeScript/JavaScript/JSON (via biome) and Python (via ruff) files.\n * See https://github.com/nrwl/nx/blob/4cd640a9187954505d12de5b6d76a90d8ce4c2eb/packages/devkit/src/generators/format-files.ts#L11\n */\nexport async function formatFilesInSubtree(\n tree: Tree,\n dir?: string,\n options?: FormatFilesInSubtreeOptions,\n): Promise<void> {\n let ignored = treeIgnoredPaths.get(tree);\n if (options?.ignore?.length) {\n if (!ignored) {\n ignored = new Set();\n treeIgnoredPaths.set(tree, ignored);\n }\n for (const filePath of options.ignore) {\n ignored.add(normalizeTreePath(filePath));\n }\n }\n const changedFiles = tree\n .listChanges()\n .filter((file) => file.type !== 'DELETE')\n .filter((file) => (dir ? file.path.startsWith(dir) : true))\n .filter((file) => !ignored?.has(normalizeTreePath(file.path)));\n\n const pyFiles = changedFiles.filter((file) => file.path.endsWith('.py'));\n const otherFiles = changedFiles.filter(\n (file) =>\n BIOME_FORMATTABLE_EXTENSIONS.has(path.extname(file.path)) &&\n // tsconfigs are not biome-managed: they're excluded from the vended\n // format target (Nx's typescript-sync rewrites them without formatting),\n // so formatting them at generation would only diverge from the form\n // written on later runs. Leave them as updateJson/writeJson emit them so\n // repeated generation stays idempotent.\n !isTsConfig(file.path),\n );\n\n // Resolve each project's ruff settings (module names, line-length) so files\n // are formatted to match the on-disk build (see getPythonProjectRuffConfigs).\n const pythonProjectConfigs = pyFiles.length\n ? getPythonProjectRuffConfigs(tree)\n : [];\n\n // Format Python files with ruff (lint fixes + formatting)\n for (const file of pyFiles) {\n try {\n const content = ruffFixAndFormat(\n file.content.toString('utf-8'),\n file.path,\n resolveRuffOptions(\n readRuffConfig(tree, file.path),\n getOwningProjectRuffConfig(file.path, pythonProjectConfigs),\n ),\n );\n tree.write(file.path, content);\n } catch {\n // Silently skip ruff formatting failures\n }\n }\n\n if (otherFiles.length === 0) return;\n\n formatWithBiome(tree, otherFiles);\n}\n\n/**\n * The Biome configuration to format with: the workspace's own `biome.json`, else\n * the config we vend.\n *\n * Read through the tree, which falls back to disk for a file it doesn't hold. So\n * this resolves the most current config either way — the workspace's on-disk one,\n * or the version a generator has just written to the tree and not yet flushed,\n * which shelling out to the CLI could never see.\n */\nfunction readBiomeConfig(tree: Tree): unknown {\n const config = tree.read('biome.json', 'utf-8');\n if (config) {\n try {\n return JSON.parse(config);\n } catch {\n // Malformed config — fall through to the config we vend\n }\n }\n return getDefaultBiomeConfig(tree);\n}\n\n/**\n * Format files with Biome in-process, reusing one instance across the batch.\n *\n * Replaces one `biome format --stdin-file-path` process per file, which\n * dominated generation at ~70ms each: `formatFilesInSubtree` formats every\n * change accumulated in the tree, not only the ones its caller made, so\n * generators sharing a tree reformat the same files once per call. In-process is\n * ~0.5ms per file. Output was verified byte-identical across the plugin's whole\n * source tree, except that the CLI corrupts control characters passed through\n * stdin (a NUL in a template literal) where formatting in-process preserves them.\n */\nfunction formatWithBiome(\n tree: Tree,\n files: { path: string; content: Buffer | null }[],\n): void {\n try {\n const biome = new Biome();\n const { projectKey } = biome.openProject();\n biome.applyConfiguration(projectKey, readBiomeConfig(tree));\n\n for (const file of files) {\n try {\n const { content } = biome.formatContent(\n projectKey,\n file.content?.toString('utf-8') ?? '',\n { filePath: file.path },\n );\n tree.write(file.path, content);\n } catch {\n // Leave individual files that fail to format untouched\n }\n }\n } catch {\n // Silently skip formatting failures\n }\n}\n\n/**\n * Read the ruff config for a file, by walking from its directory up to the\n * workspace root looking for `.ruff.toml`, `ruff.toml`, or a `pyproject.toml`\n * with a `[tool.ruff]` section — the same files, in the same order, that ruff\n * itself resolves. The walk stops at the workspace root so a stray config in a\n * parent of the workspace (or the home directory) is never treated as the\n * project's.\n *\n * The settings are read rather than merely detected because formatting runs\n * in-process against tree content, so ruff never sees the file's location and\n * cannot resolve the config itself (see {@link resolveRuffOptions}).\n *\n * Reads go through the tree, which falls through to disk for files this run has\n * not touched, so a config the generator has just written in memory is picked up\n * as well as one already on disk.\n */\nfunction readRuffConfig(tree: Tree, filePath: string): RuffOptions | undefined {\n let dir = path.dirname(filePath);\n while (true) {\n for (const name of ['.ruff.toml', 'ruff.toml']) {\n const config = tryReadToml(tree, path.join(dir, name));\n if (config) {\n return config as RuffOptions;\n }\n }\n const ruff = (tryReadToml(tree, path.join(dir, 'pyproject.toml')) as any)\n ?.tool?.ruff;\n if (ruff) {\n return ruff as RuffOptions;\n }\n // Stop once the workspace root ('.') has been checked.\n if (dir === '.' || dir === '' || dir === path.dirname(dir)) {\n return undefined;\n }\n dir = path.dirname(dir);\n }\n}\n\n/**\n * The `[tool.ruff.lint].select` generated Python projects vend. Generation\n * formats before that config lands on disk, so it is pinned here to keep\n * generated files clean under the project's own `lint` target rather than under\n * whatever ruff's defaults happen to be for the pinned release.\n */\nconst DEFAULT_RUFF_SELECT = ['E', 'F', 'UP', 'B', 'SIM', 'I'];\n\ninterface PythonProjectRuffConfig {\n /** Project root, normalised to use forward slashes. */\n readonly root: string;\n /** Top-level importable module names declared by the project. */\n readonly modules: string[];\n /** The project's `[tool.ruff].line-length`, if set. */\n readonly lineLength?: number;\n /**\n * Ruff `target-version` (eg `py314`) derived from `[project].requires-python`.\n * Generation formats via stdin with no pyproject, so it must be passed\n * explicitly — ruff's formatting differs by target.\n */\n readonly targetVersion?: string;\n /** The project's `[tool.ruff.lint].select`, if set. */\n readonly select?: string[];\n}\n\n/**\n * Derive ruff's `target-version` (eg `py314`) from a PEP 508\n * `requires-python` specifier (eg `>=3.14`). Ruff targets the minimum\n * supported version, so take the lowest `major.minor` mentioned.\n */\nexport const requiresPythonToRuffTarget = (\n requiresPython: unknown,\n): string | undefined => {\n if (typeof requiresPython !== 'string') {\n return undefined;\n }\n let min: { major: number; minor: number } | undefined;\n for (const match of requiresPython.matchAll(/(\\d+)\\.(\\d+)/g)) {\n const major = Number(match[1]);\n const minor = Number(match[2]);\n if (\n !min ||\n major < min.major ||\n (major === min.major && minor < min.minor)\n ) {\n min = { major, minor };\n }\n }\n return min ? `py${min.major}${min.minor}` : undefined;\n};\n\n/**\n * Map each Nx project with a `pyproject.toml` to the ruff settings the on-disk\n * build enforces for it: its top-level module names (from\n * `[tool.hatch.build.targets.wheel].packages`), its `[tool.ruff].line-length`\n * and its `[tool.ruff.lint].select`.\n */\nfunction getPythonProjectRuffConfigs(tree: Tree): PythonProjectRuffConfig[] {\n const configs: PythonProjectRuffConfig[] = [];\n\n for (const project of getProjects(tree).values()) {\n // Projects without a pyproject.toml, or whose one cannot be parsed, have no\n // ruff settings to contribute.\n const pyproject = tryReadToml(\n tree,\n path.join(project.root, 'pyproject.toml'),\n ) as any;\n if (!pyproject) {\n continue;\n }\n const wheelPackages: unknown =\n pyproject?.tool?.hatch?.build?.targets?.wheel?.packages;\n // Record the top-level module segment (`pkg/sub` -> `pkg`), which is\n // all `known-first-party` keys off.\n const modules = Array.isArray(wheelPackages)\n ? wheelPackages\n .filter((pkg): pkg is string => typeof pkg === 'string' && !!pkg)\n .map((pkg) => pkg.split('/')[0])\n : [];\n const lineLength: unknown = pyproject?.tool?.ruff?.['line-length'];\n const targetVersion = requiresPythonToRuffTarget(\n pyproject?.project?.['requires-python'],\n );\n const selected: unknown = pyproject?.tool?.ruff?.lint?.select;\n const select = Array.isArray(selected)\n ? selected.filter(\n (rule): rule is string => typeof rule === 'string' && !!rule,\n )\n : undefined;\n if (\n modules.length ||\n typeof lineLength === 'number' ||\n targetVersion ||\n select?.length\n ) {\n configs.push({\n root: project.root.split(path.sep).join('/'),\n modules,\n lineLength: typeof lineLength === 'number' ? lineLength : undefined,\n targetVersion,\n select: select?.length ? select : undefined,\n });\n }\n }\n\n return configs;\n}\n\n/**\n * Resolve the ruff config for the project that owns a file (the project with\n * the longest root that is a prefix of the file path). Ruff runs per-project on\n * disk, so a file's settings come from its own project — only its own module is\n * first-party (sibling workspace packages are third-party) and its own\n * line-length applies — and scoping this way keeps in-tree formatting\n * consistent with the on-disk build.\n */\nfunction getOwningProjectRuffConfig(\n filePath: string,\n configs: PythonProjectRuffConfig[],\n): PythonProjectRuffConfig | undefined {\n let owner: PythonProjectRuffConfig | undefined;\n for (const config of configs) {\n if (\n (filePath === config.root || filePath.startsWith(`${config.root}/`)) &&\n (!owner || config.root.length > owner.root.length)\n ) {\n owner = config;\n }\n }\n return owner;\n}\n\n/**\n * Resolve the ruff settings to format a Python file with, mirroring what the\n * file's build enforces.\n *\n * Ruff's own config discovery never runs: settings are passed to the linter\n * directly rather than resolved from the file's location, which it never sees.\n * That also means a stray config above the workspace (or in the home directory)\n * can never be picked up.\n *\n * `config` is the file's nearest ruff config, whose rule selection is honoured\n * as-is. Without one, ruff would fall back to its own defaults, which change\n * between releases — 0.16 widened them from `E` and `F` to 36 rule prefixes — so\n * generation would apply fixes the project's build never asks for (eg `RUF022`\n * reordering `__all__`). Instead the project's own `lint.select` is pinned, so\n * generation enforces exactly what `lint` does and nothing more.\n *\n * `projectConfig` layers on the settings derived from the owning project rather\n * than declared under `[tool.ruff]`: `known-first-party` (the project's own\n * modules, from its wheel packages) keeps its imports in their own group, and\n * `target-version` (from its `requires-python`) matches the formatting the build\n * produces.\n */\nfunction resolveRuffOptions(\n config: RuffOptions | undefined,\n projectConfig?: PythonProjectRuffConfig,\n): RuffOptions {\n const lint: Record<string, unknown> = { ...config?.lint };\n // Pin the rule selection only when deferring to ruff's defaults would\n // otherwise apply rules the project's build does not enforce.\n if (!config) {\n lint.select = projectConfig?.select ?? DEFAULT_RUFF_SELECT;\n }\n if (projectConfig?.modules.length) {\n lint.isort = {\n ...(lint.isort as object | undefined),\n 'known-first-party': projectConfig.modules,\n };\n }\n return {\n ...config,\n ...(typeof projectConfig?.lineLength === 'number'\n ? { 'line-length': projectConfig.lineLength }\n : {}),\n ...(projectConfig?.targetVersion\n ? { 'target-version': projectConfig.targetVersion }\n : {}),\n lint,\n };\n}\n"],"names":["Biome","getProjects","createRequire","path","ruffFixAndFormat","tryReadToml","TS_VERSIONS","BIOME_WASM_VERSION","url","version","BIOME_TEST_OUTPUT_EXCLUDE","getDefaultBiomeConfig","tree","$schema","root","formatter","enabled","indentStyle","indentWidth","lineWidth","javascript","quoteStyle","trailingCommas","exists","resolver","experimentalPnpmCatalogs","css","linter","rules","preset","correctness","noUndeclaredDependencies","assist","actions","source","organizeImports","files","includes","overrides","BIOME_FORMATTABLE_EXTENSIONS","Set","isTsConfig","filePath","test","treeIgnoredPaths","WeakMap","normalizeTreePath","split","sep","join","replace","formatFilesInSubtree","dir","options","ignored","get","ignore","length","set","add","changedFiles","listChanges","filter","file","type","startsWith","has","pyFiles","endsWith","otherFiles","extname","pythonProjectConfigs","getPythonProjectRuffConfigs","content","toString","resolveRuffOptions","readRuffConfig","getOwningProjectRuffConfig","write","formatWithBiome","readBiomeConfig","config","read","JSON","parse","biome","projectKey","openProject","applyConfiguration","formatContent","dirname","name","ruff","tool","undefined","DEFAULT_RUFF_SELECT","requiresPythonToRuffTarget","requiresPython","min","match","matchAll","major","Number","minor","configs","project","values","pyproject","wheelPackages","hatch","build","targets","wheel","packages","modules","Array","isArray","pkg","map","lineLength","targetVersion","selected","lint","select","rule","push","owner","projectConfig","isort"],"mappings":"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"}
|
package/src/utils/fs.d.ts
CHANGED
|
@@ -6,13 +6,7 @@ import type { Tree } from '@nx/devkit';
|
|
|
6
6
|
import { type DependencyDeclaration, type MustDeclare } from './declared-dependencies.js';
|
|
7
7
|
/** Dependencies a caller must declare to use `FsCommands`. */
|
|
8
8
|
export declare const FS_DEPENDENCIES: readonly [{
|
|
9
|
-
readonly name: "
|
|
10
|
-
}, {
|
|
11
|
-
readonly name: "rimraf";
|
|
12
|
-
}, {
|
|
13
|
-
readonly name: "make-dir-cli";
|
|
14
|
-
}, {
|
|
15
|
-
readonly name: "cpy-cli";
|
|
9
|
+
readonly name: "shx";
|
|
16
10
|
}];
|
|
17
11
|
/**
|
|
18
12
|
* Platform agnostic commands for filesystem operations, adding the CLIs they
|
|
@@ -22,13 +16,29 @@ export declare class FsCommands<D extends DependencyDeclaration> {
|
|
|
22
16
|
private tree;
|
|
23
17
|
private declaration;
|
|
24
18
|
constructor(tree: Tree, declaration: D & MustDeclare<typeof FS_DEPENDENCIES, D>);
|
|
25
|
-
|
|
19
|
+
/** Copy a single file to `dst`, which names the file rather than a directory. */
|
|
20
|
+
cpFile(src: string, dst: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Copy a directory's contents into `dst`, merging into whatever is already
|
|
23
|
+
* there.
|
|
24
|
+
*
|
|
25
|
+
* The trailing `/.` is load bearing: `shx cp -R src dst` follows POSIX `cp`,
|
|
26
|
+
* which nests `src` *inside* an existing `dst` rather than merging into it, so
|
|
27
|
+
* without it a copy into a directory a preceding `mkdir` had just created
|
|
28
|
+
* would land a level too deep. `src/.` copies the contents, and unlike a
|
|
29
|
+
* `src/*` glob it includes dotfiles — which `node_modules/.bin` depends on.
|
|
30
|
+
*/
|
|
31
|
+
cpDir(src: string, dst: string): string;
|
|
26
32
|
rm(dir: string): string;
|
|
27
33
|
mkdir(dir: string): string;
|
|
28
34
|
/**
|
|
29
|
-
* Copy the single file a glob matches to `
|
|
30
|
-
* names it — the Smithy OpenAPI plugin writes
|
|
31
|
-
* Fails rather than silently doing nothing when
|
|
35
|
+
* Copy the single file a glob matches to `dstDir/dstFileName`, for an artifact
|
|
36
|
+
* whose producer names it — the Smithy OpenAPI plugin writes
|
|
37
|
+
* `<ServiceShape>.openapi.json`. Fails rather than silently doing nothing when
|
|
38
|
+
* the glob matches nothing, or when it matches more than one file.
|
|
39
|
+
*
|
|
40
|
+
* The caller must {@link mkdir} `dstDir` first: `shx cp` writes through to a
|
|
41
|
+
* named destination file and fails if its parent is missing.
|
|
32
42
|
*/
|
|
33
43
|
cpGlobToFile(srcGlob: string, dstDir: string, dstFileName: string): string;
|
|
34
44
|
private add;
|
package/src/utils/fs.js
CHANGED
|
@@ -6,16 +6,7 @@ import { addDependenciesToPackageJson } from "./dependencies.js";
|
|
|
6
6
|
import { withVersions } from "./versions.js";
|
|
7
7
|
/** Dependencies a caller must declare to use `FsCommands`. */ export const FS_DEPENDENCIES = [
|
|
8
8
|
{
|
|
9
|
-
name: '
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
name: 'rimraf'
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
name: 'make-dir-cli'
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
name: 'cpy-cli'
|
|
9
|
+
name: 'shx'
|
|
19
10
|
}
|
|
20
11
|
];
|
|
21
12
|
/**
|
|
@@ -28,25 +19,42 @@ import { withVersions } from "./versions.js";
|
|
|
28
19
|
this.tree = tree;
|
|
29
20
|
this.declaration = declaration;
|
|
30
21
|
}
|
|
31
|
-
|
|
32
|
-
this.add('
|
|
33
|
-
return `
|
|
22
|
+
/** Copy a single file to `dst`, which names the file rather than a directory. */ cpFile(src, dst) {
|
|
23
|
+
this.add('shx');
|
|
24
|
+
return `shx cp ${src} ${dst}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Copy a directory's contents into `dst`, merging into whatever is already
|
|
28
|
+
* there.
|
|
29
|
+
*
|
|
30
|
+
* The trailing `/.` is load bearing: `shx cp -R src dst` follows POSIX `cp`,
|
|
31
|
+
* which nests `src` *inside* an existing `dst` rather than merging into it, so
|
|
32
|
+
* without it a copy into a directory a preceding `mkdir` had just created
|
|
33
|
+
* would land a level too deep. `src/.` copies the contents, and unlike a
|
|
34
|
+
* `src/*` glob it includes dotfiles — which `node_modules/.bin` depends on.
|
|
35
|
+
*/ cpDir(src, dst) {
|
|
36
|
+
this.add('shx');
|
|
37
|
+
return `shx cp -R ${src}/. ${dst}`;
|
|
34
38
|
}
|
|
35
39
|
rm(dir) {
|
|
36
|
-
this.add('
|
|
37
|
-
return `
|
|
40
|
+
this.add('shx');
|
|
41
|
+
return `shx rm -rf ${dir}`;
|
|
38
42
|
}
|
|
39
43
|
mkdir(dir) {
|
|
40
|
-
this.add('
|
|
41
|
-
return `
|
|
44
|
+
this.add('shx');
|
|
45
|
+
return `shx mkdir -p ${dir}`;
|
|
42
46
|
}
|
|
43
47
|
/**
|
|
44
|
-
* Copy the single file a glob matches to `
|
|
45
|
-
* names it — the Smithy OpenAPI plugin writes
|
|
46
|
-
* Fails rather than silently doing nothing when
|
|
48
|
+
* Copy the single file a glob matches to `dstDir/dstFileName`, for an artifact
|
|
49
|
+
* whose producer names it — the Smithy OpenAPI plugin writes
|
|
50
|
+
* `<ServiceShape>.openapi.json`. Fails rather than silently doing nothing when
|
|
51
|
+
* the glob matches nothing, or when it matches more than one file.
|
|
52
|
+
*
|
|
53
|
+
* The caller must {@link mkdir} `dstDir` first: `shx cp` writes through to a
|
|
54
|
+
* named destination file and fails if its parent is missing.
|
|
47
55
|
*/ cpGlobToFile(srcGlob, dstDir, dstFileName) {
|
|
48
|
-
this.add('
|
|
49
|
-
return `
|
|
56
|
+
this.add('shx');
|
|
57
|
+
return `shx cp "${srcGlob}" ${dstDir}/${dstFileName}`;
|
|
50
58
|
}
|
|
51
59
|
add(dep) {
|
|
52
60
|
addDependenciesToPackageJson(this.tree, {}, withVersions(forDependencies(this.declaration), [
|
package/src/utils/fs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/fs.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { Tree } from '@nx/devkit';\nimport {\n type DependencyDeclaration,\n forDependencies,\n type MustDeclare,\n} from './declared-dependencies.js';\nimport { addDependenciesToPackageJson } from './dependencies.js';\nimport { type ITsDepVersion, withVersions } from './versions.js';\n\n/** Dependencies a caller must declare to use `FsCommands`. */\nexport const FS_DEPENDENCIES = [
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/fs.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { Tree } from '@nx/devkit';\nimport {\n type DependencyDeclaration,\n forDependencies,\n type MustDeclare,\n} from './declared-dependencies.js';\nimport { addDependenciesToPackageJson } from './dependencies.js';\nimport { type ITsDepVersion, withVersions } from './versions.js';\n\n/** Dependencies a caller must declare to use `FsCommands`. */\nexport const FS_DEPENDENCIES = [{ name: 'shx' }] as const satisfies readonly {\n name: ITsDepVersion;\n}[];\n\n/**\n * Platform agnostic commands for filesystem operations, adding the CLIs they\n * need to the root package.json.\n */\nexport class FsCommands<D extends DependencyDeclaration> {\n private tree: Tree;\n private declaration: D;\n\n constructor(\n tree: Tree,\n declaration: D & MustDeclare<typeof FS_DEPENDENCIES, D>,\n ) {\n this.tree = tree;\n this.declaration = declaration;\n }\n\n /** Copy a single file to `dst`, which names the file rather than a directory. */\n public cpFile(src: string, dst: string) {\n this.add('shx');\n return `shx cp ${src} ${dst}`;\n }\n\n /**\n * Copy a directory's contents into `dst`, merging into whatever is already\n * there.\n *\n * The trailing `/.` is load bearing: `shx cp -R src dst` follows POSIX `cp`,\n * which nests `src` *inside* an existing `dst` rather than merging into it, so\n * without it a copy into a directory a preceding `mkdir` had just created\n * would land a level too deep. `src/.` copies the contents, and unlike a\n * `src/*` glob it includes dotfiles — which `node_modules/.bin` depends on.\n */\n public cpDir(src: string, dst: string) {\n this.add('shx');\n return `shx cp -R ${src}/. ${dst}`;\n }\n\n public rm(dir: string) {\n this.add('shx');\n return `shx rm -rf ${dir}`;\n }\n\n public mkdir(dir: string) {\n this.add('shx');\n return `shx mkdir -p ${dir}`;\n }\n\n /**\n * Copy the single file a glob matches to `dstDir/dstFileName`, for an artifact\n * whose producer names it — the Smithy OpenAPI plugin writes\n * `<ServiceShape>.openapi.json`. Fails rather than silently doing nothing when\n * the glob matches nothing, or when it matches more than one file.\n *\n * The caller must {@link mkdir} `dstDir` first: `shx cp` writes through to a\n * named destination file and fails if its parent is missing.\n */\n public cpGlobToFile(srcGlob: string, dstDir: string, dstFileName: string) {\n this.add('shx');\n return `shx cp \"${srcGlob}\" ${dstDir}/${dstFileName}`;\n }\n\n private add(dep: (typeof FS_DEPENDENCIES)[number]['name']) {\n addDependenciesToPackageJson(\n this.tree,\n {},\n withVersions(forDependencies<typeof FS_DEPENDENCIES>(this.declaration), [\n dep,\n ]),\n );\n }\n}\n"],"names":["forDependencies","addDependenciesToPackageJson","withVersions","FS_DEPENDENCIES","name","FsCommands","tree","declaration","cpFile","src","dst","add","cpDir","rm","dir","mkdir","cpGlobToFile","srcGlob","dstDir","dstFileName","dep"],"mappings":"AAAA;;;CAGC,GAED,SAEEA,eAAe,QAEV,6BAA6B;AACpC,SAASC,4BAA4B,QAAQ,oBAAoB;AACjE,SAA6BC,YAAY,QAAQ,gBAAgB;AAEjE,4DAA4D,GAC5D,OAAO,MAAMC,kBAAkB;IAAC;QAAEC,MAAM;IAAM;CAAE,CAE5C;AAEJ;;;CAGC,GACD,OAAO,MAAMC;IACHC,KAAW;IACXC,YAAe;IAEvB,YACED,IAAU,EACVC,WAAuD,CACvD;QACA,IAAI,CAACD,IAAI,GAAGA;QACZ,IAAI,CAACC,WAAW,GAAGA;IACrB;IAEA,+EAA+E,GAC/E,AAAOC,OAAOC,GAAW,EAAEC,GAAW,EAAE;QACtC,IAAI,CAACC,GAAG,CAAC;QACT,OAAO,CAAC,OAAO,EAAEF,IAAI,CAAC,EAAEC,KAAK;IAC/B;IAEA;;;;;;;;;GASC,GACD,AAAOE,MAAMH,GAAW,EAAEC,GAAW,EAAE;QACrC,IAAI,CAACC,GAAG,CAAC;QACT,OAAO,CAAC,UAAU,EAAEF,IAAI,GAAG,EAAEC,KAAK;IACpC;IAEOG,GAAGC,GAAW,EAAE;QACrB,IAAI,CAACH,GAAG,CAAC;QACT,OAAO,CAAC,WAAW,EAAEG,KAAK;IAC5B;IAEOC,MAAMD,GAAW,EAAE;QACxB,IAAI,CAACH,GAAG,CAAC;QACT,OAAO,CAAC,aAAa,EAAEG,KAAK;IAC9B;IAEA;;;;;;;;GAQC,GACD,AAAOE,aAAaC,OAAe,EAAEC,MAAc,EAAEC,WAAmB,EAAE;QACxE,IAAI,CAACR,GAAG,CAAC;QACT,OAAO,CAAC,QAAQ,EAAEM,QAAQ,EAAE,EAAEC,OAAO,CAAC,EAAEC,aAAa;IACvD;IAEQR,IAAIS,GAA6C,EAAE;QACzDnB,6BACE,IAAI,CAACK,IAAI,EACT,CAAC,GACDJ,aAAaF,gBAAwC,IAAI,CAACO,WAAW,GAAG;YACtEa;SACD;IAEL;AACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IJavaVersion, IMiseVersion, IPyDepVersion, ITsDepVersion } from '../versions.js';
|
|
2
|
+
/** Any dependency a version table pins. */
|
|
3
|
+
export type IVersionedDep = ITsDepVersion | IPyDepVersion | IJavaVersion | IMiseVersion;
|
|
4
|
+
/** A pin the run deliberately did not take, and why. */
|
|
5
|
+
export interface LockstepNote {
|
|
6
|
+
name: IVersionedDep;
|
|
7
|
+
note: string;
|
|
8
|
+
}
|
|
9
|
+
/** Proposed versions from a run, keyed by dependency name. */
|
|
10
|
+
export type ProposedVersions = Partial<Record<IVersionedDep, string>>;
|
|
11
|
+
/**
|
|
12
|
+
* Dependencies that must move as a unit. At least two, since one pin has nothing
|
|
13
|
+
* to stay in step with.
|
|
14
|
+
*/
|
|
15
|
+
export type LockstepGroup = readonly [
|
|
16
|
+
IVersionedDep,
|
|
17
|
+
IVersionedDep,
|
|
18
|
+
...IVersionedDep[]
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Hold every member of each group at the lowest version proposed across it, so a
|
|
22
|
+
* group only moves once all of its members can.
|
|
23
|
+
*
|
|
24
|
+
* Mutates the tables, which hold proposed versions not yet written anywhere.
|
|
25
|
+
* Groups the run proposed fewer than two comparable versions for are left alone.
|
|
26
|
+
*/
|
|
27
|
+
export declare const holdGroupsInLockstep: (groups: readonly LockstepGroup[], ...tables: readonly ProposedVersions[]) => LockstepNote[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/ import { coerce, compare } from "semver";
|
|
5
|
+
/** The bare version in a pin, whatever range syntax it was written with. */ const bare = (version)=>coerce(version, {
|
|
6
|
+
includePrerelease: true
|
|
7
|
+
})?.version;
|
|
8
|
+
/** The range prefix a pin was written with, so it is preserved. */ const prefixOf = (version, bareVersion)=>version.slice(0, version.indexOf(bareVersion));
|
|
9
|
+
/**
|
|
10
|
+
* Hold every member of each group at the lowest version proposed across it, so a
|
|
11
|
+
* group only moves once all of its members can.
|
|
12
|
+
*
|
|
13
|
+
* Mutates the tables, which hold proposed versions not yet written anywhere.
|
|
14
|
+
* Groups the run proposed fewer than two comparable versions for are left alone.
|
|
15
|
+
*/ export const holdGroupsInLockstep = (groups, ...tables)=>{
|
|
16
|
+
const notes = [];
|
|
17
|
+
for (const group of groups){
|
|
18
|
+
const members = group.flatMap((name)=>{
|
|
19
|
+
const table = tables.find((candidate)=>candidate[name] !== undefined);
|
|
20
|
+
const proposed = table?.[name];
|
|
21
|
+
const version = proposed && bare(proposed);
|
|
22
|
+
return table && proposed && version ? [
|
|
23
|
+
{
|
|
24
|
+
name,
|
|
25
|
+
table,
|
|
26
|
+
proposed,
|
|
27
|
+
version
|
|
28
|
+
}
|
|
29
|
+
] : [];
|
|
30
|
+
});
|
|
31
|
+
if (members.length < 2) continue;
|
|
32
|
+
const lowest = members.map(({ version })=>version).reduce((min, version)=>compare(version, min) < 0 ? version : min);
|
|
33
|
+
for (const { name, table, proposed, version } of members){
|
|
34
|
+
if (version === lowest) continue;
|
|
35
|
+
const held = `${prefixOf(proposed, version)}${lowest}`;
|
|
36
|
+
table[name] = held;
|
|
37
|
+
notes.push({
|
|
38
|
+
name,
|
|
39
|
+
note: `${name} held at ${held} (${proposed} available): must move in step with ${group.filter((other)=>other !== name).join(', ')}`
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return notes;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=lockstep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/version-lockstep/lockstep.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { coerce, compare } from 'semver';\nimport type {\n IJavaVersion,\n IMiseVersion,\n IPyDepVersion,\n ITsDepVersion,\n} from '../versions.js';\n\n/** Any dependency a version table pins. */\nexport type IVersionedDep =\n | ITsDepVersion\n | IPyDepVersion\n | IJavaVersion\n | IMiseVersion;\n\n/** A pin the run deliberately did not take, and why. */\nexport interface LockstepNote {\n name: IVersionedDep;\n note: string;\n}\n\n/** Proposed versions from a run, keyed by dependency name. */\nexport type ProposedVersions = Partial<Record<IVersionedDep, string>>;\n\n/**\n * Dependencies that must move as a unit. At least two, since one pin has nothing\n * to stay in step with.\n */\nexport type LockstepGroup = readonly [\n IVersionedDep,\n IVersionedDep,\n ...IVersionedDep[],\n];\n\n/** The bare version in a pin, whatever range syntax it was written with. */\nconst bare = (version: string): string | undefined =>\n coerce(version, { includePrerelease: true })?.version;\n\n/** The range prefix a pin was written with, so it is preserved. */\nconst prefixOf = (version: string, bareVersion: string): string =>\n version.slice(0, version.indexOf(bareVersion));\n\n/**\n * Hold every member of each group at the lowest version proposed across it, so a\n * group only moves once all of its members can.\n *\n * Mutates the tables, which hold proposed versions not yet written anywhere.\n * Groups the run proposed fewer than two comparable versions for are left alone.\n */\nexport const holdGroupsInLockstep = (\n groups: readonly LockstepGroup[],\n ...tables: readonly ProposedVersions[]\n): LockstepNote[] => {\n const notes: LockstepNote[] = [];\n\n for (const group of groups) {\n const members = group.flatMap((name) => {\n const table = tables.find((candidate) => candidate[name] !== undefined);\n const proposed = table?.[name];\n const version = proposed && bare(proposed);\n return table && proposed && version\n ? [{ name, table, proposed, version }]\n : [];\n });\n if (members.length < 2) continue;\n\n const lowest = members\n .map(({ version }) => version)\n .reduce((min, version) => (compare(version, min) < 0 ? version : min));\n\n for (const { name, table, proposed, version } of members) {\n if (version === lowest) continue;\n const held = `${prefixOf(proposed, version)}${lowest}`;\n table[name] = held;\n notes.push({\n name,\n note: `${name} held at ${held} (${proposed} available): must move in step with ${group\n .filter((other) => other !== name)\n .join(', ')}`,\n });\n }\n }\n\n return notes;\n};\n"],"names":["coerce","compare","bare","version","includePrerelease","prefixOf","bareVersion","slice","indexOf","holdGroupsInLockstep","groups","tables","notes","group","members","flatMap","name","table","find","candidate","undefined","proposed","length","lowest","map","reduce","min","held","push","note","filter","other","join"],"mappings":"AAAA;;;CAGC,GACD,SAASA,MAAM,EAAEC,OAAO,QAAQ,SAAS;AAkCzC,0EAA0E,GAC1E,MAAMC,OAAO,CAACC,UACZH,OAAOG,SAAS;QAAEC,mBAAmB;IAAK,IAAID;AAEhD,iEAAiE,GACjE,MAAME,WAAW,CAACF,SAAiBG,cACjCH,QAAQI,KAAK,CAAC,GAAGJ,QAAQK,OAAO,CAACF;AAEnC;;;;;;CAMC,GACD,OAAO,MAAMG,uBAAuB,CAClCC,QACA,GAAGC;IAEH,MAAMC,QAAwB,EAAE;IAEhC,KAAK,MAAMC,SAASH,OAAQ;QAC1B,MAAMI,UAAUD,MAAME,OAAO,CAAC,CAACC;YAC7B,MAAMC,QAAQN,OAAOO,IAAI,CAAC,CAACC,YAAcA,SAAS,CAACH,KAAK,KAAKI;YAC7D,MAAMC,WAAWJ,OAAO,CAACD,KAAK;YAC9B,MAAMb,UAAUkB,YAAYnB,KAAKmB;YACjC,OAAOJ,SAASI,YAAYlB,UACxB;gBAAC;oBAAEa;oBAAMC;oBAAOI;oBAAUlB;gBAAQ;aAAE,GACpC,EAAE;QACR;QACA,IAAIW,QAAQQ,MAAM,GAAG,GAAG;QAExB,MAAMC,SAAST,QACZU,GAAG,CAAC,CAAC,EAAErB,OAAO,EAAE,GAAKA,SACrBsB,MAAM,CAAC,CAACC,KAAKvB,UAAaF,QAAQE,SAASuB,OAAO,IAAIvB,UAAUuB;QAEnE,KAAK,MAAM,EAAEV,IAAI,EAAEC,KAAK,EAAEI,QAAQ,EAAElB,OAAO,EAAE,IAAIW,QAAS;YACxD,IAAIX,YAAYoB,QAAQ;YACxB,MAAMI,OAAO,GAAGtB,SAASgB,UAAUlB,WAAWoB,QAAQ;YACtDN,KAAK,CAACD,KAAK,GAAGW;YACdf,MAAMgB,IAAI,CAAC;gBACTZ;gBACAa,MAAM,GAAGb,KAAK,SAAS,EAAEW,KAAK,EAAE,EAAEN,SAAS,oCAAoC,EAAER,MAC9EiB,MAAM,CAAC,CAACC,QAAUA,UAAUf,MAC5BgB,IAAI,CAAC,OAAO;YACjB;QACF;IACF;IAEA,OAAOpB;AACT,EAAE"}
|
package/src/utils/versions.d.ts
CHANGED
|
@@ -94,7 +94,6 @@ export declare const TS_VERSIONS: {
|
|
|
94
94
|
readonly 'class-variance-authority': "0.7.1";
|
|
95
95
|
readonly clsx: "2.1.1";
|
|
96
96
|
readonly commander: "15.0.0";
|
|
97
|
-
readonly 'cpy-cli': "7.0.0";
|
|
98
97
|
readonly electrodb: "3.9.3";
|
|
99
98
|
readonly esbuild: "0.28.2";
|
|
100
99
|
readonly 'event-source-polyfill': "1.0.31";
|
|
@@ -110,10 +109,8 @@ export declare const TS_VERSIONS: {
|
|
|
110
109
|
readonly husky: "9.1.7";
|
|
111
110
|
readonly 'fs-extra': "11.4.0";
|
|
112
111
|
readonly '@types/fs-extra': "11.0.4";
|
|
113
|
-
readonly 'make-dir-cli': "4.0.0";
|
|
114
112
|
readonly mariadb: "3.5.3";
|
|
115
113
|
readonly mise: "2026.8.14";
|
|
116
|
-
readonly ncp: "2.0.0";
|
|
117
114
|
readonly npm: "12.0.2";
|
|
118
115
|
readonly 'npm-check-updates': "23.1.0";
|
|
119
116
|
readonly 'oidc-client-ts': "3.5.0";
|
|
@@ -122,9 +119,9 @@ export declare const TS_VERSIONS: {
|
|
|
122
119
|
readonly 'react-oidc-context': "3.3.1";
|
|
123
120
|
readonly react: "19.2.8";
|
|
124
121
|
readonly 'react-dom': "19.2.8";
|
|
125
|
-
readonly rimraf: "6.1.3";
|
|
126
122
|
readonly rolldown: "1.2.6";
|
|
127
123
|
readonly 'rolldown-plugin-dts': "0.28.3";
|
|
124
|
+
readonly shx: "0.4.0";
|
|
128
125
|
readonly 'simple-git': "3.36.0";
|
|
129
126
|
readonly 'source-map-support': "0.5.21";
|
|
130
127
|
readonly 'starlight-blog': "0.29.0";
|
|
@@ -143,6 +140,14 @@ export declare const TS_VERSIONS: {
|
|
|
143
140
|
readonly ws: "8.21.3";
|
|
144
141
|
};
|
|
145
142
|
export type ITsDepVersion = keyof typeof TS_VERSIONS;
|
|
143
|
+
/**
|
|
144
|
+
* Dependencies that must move as a unit, held at the lowest version proposed
|
|
145
|
+
* across each group by the version update. Members must share a version line.
|
|
146
|
+
*
|
|
147
|
+
* The wasm formatter bindings are dependencies of this repo rather than vended
|
|
148
|
+
* pins, so they are named here but versioned in the manifests.
|
|
149
|
+
*/
|
|
150
|
+
export declare const LOCKSTEP_GROUPS: readonly [readonly ["@ag-ui/client", "@ag-ui/core", "@ag-ui/encoder"], readonly ["@biomejs/wasm-nodejs", "@biomejs/biome"], readonly ["@astral-sh/ruff-wasm-nodejs", "ruff"]];
|
|
146
151
|
/**
|
|
147
152
|
* Add versions to the given dependencies, which the declaration must own.
|
|
148
153
|
*
|
package/src/utils/versions.js
CHANGED
|
@@ -100,7 +100,6 @@
|
|
|
100
100
|
'class-variance-authority': '0.7.1',
|
|
101
101
|
clsx: '2.1.1',
|
|
102
102
|
commander: '15.0.0',
|
|
103
|
-
'cpy-cli': '7.0.0',
|
|
104
103
|
electrodb: '3.9.3',
|
|
105
104
|
esbuild: '0.28.2',
|
|
106
105
|
'event-source-polyfill': '1.0.31',
|
|
@@ -116,10 +115,8 @@
|
|
|
116
115
|
husky: '9.1.7',
|
|
117
116
|
'fs-extra': '11.4.0',
|
|
118
117
|
'@types/fs-extra': '11.0.4',
|
|
119
|
-
'make-dir-cli': '4.0.0',
|
|
120
118
|
mariadb: '3.5.3',
|
|
121
119
|
mise: '2026.8.14',
|
|
122
|
-
ncp: '2.0.0',
|
|
123
120
|
npm: '12.0.2',
|
|
124
121
|
'npm-check-updates': '23.1.0',
|
|
125
122
|
'oidc-client-ts': '3.5.0',
|
|
@@ -128,9 +125,9 @@
|
|
|
128
125
|
'react-oidc-context': '3.3.1',
|
|
129
126
|
react: '19.2.8',
|
|
130
127
|
'react-dom': '19.2.8',
|
|
131
|
-
rimraf: '6.1.3',
|
|
132
128
|
rolldown: '1.2.6',
|
|
133
129
|
'rolldown-plugin-dts': '0.28.3',
|
|
130
|
+
shx: '0.4.0',
|
|
134
131
|
'simple-git': '3.36.0',
|
|
135
132
|
'source-map-support': '0.5.21',
|
|
136
133
|
'starlight-blog': '0.29.0',
|
|
@@ -148,6 +145,29 @@
|
|
|
148
145
|
zod: '4.5.4',
|
|
149
146
|
ws: '8.21.3'
|
|
150
147
|
};
|
|
148
|
+
/**
|
|
149
|
+
* Dependencies that must move as a unit, held at the lowest version proposed
|
|
150
|
+
* across each group by the version update. Members must share a version line.
|
|
151
|
+
*
|
|
152
|
+
* The wasm formatter bindings are dependencies of this repo rather than vended
|
|
153
|
+
* pins, so they are named here but versioned in the manifests.
|
|
154
|
+
*/ export const LOCKSTEP_GROUPS = [
|
|
155
|
+
// A duplicate `@ag-ui/client` fails every generated website with TS2322.
|
|
156
|
+
[
|
|
157
|
+
'@ag-ui/client',
|
|
158
|
+
'@ag-ui/core',
|
|
159
|
+
'@ag-ui/encoder'
|
|
160
|
+
],
|
|
161
|
+
// The wasm bindings format generated files; the CLI checks them.
|
|
162
|
+
[
|
|
163
|
+
'@biomejs/wasm-nodejs',
|
|
164
|
+
'@biomejs/biome'
|
|
165
|
+
],
|
|
166
|
+
[
|
|
167
|
+
'@astral-sh/ruff-wasm-nodejs',
|
|
168
|
+
'ruff'
|
|
169
|
+
]
|
|
170
|
+
];
|
|
151
171
|
/**
|
|
152
172
|
* Add versions to the given dependencies, which the declaration must own.
|
|
153
173
|
*
|