@aws/nx-plugin 1.0.0-rc.89 → 1.0.0-rc.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -22
- package/executors.json +5 -0
- package/generators.json +2 -1
- package/migrations.json +47 -2
- package/package.json +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +24 -12
- package/src/infra/app/generator.js +9 -7
- package/src/infra/app/generator.js.map +1 -1
- package/src/mcp-server/generator-info.d.ts +5 -0
- package/src/mcp-server/generator-info.js +6 -2
- package/src/mcp-server/generator-info.js.map +1 -1
- package/src/migrations/latest/add-assemble-target/metadata.json +3 -0
- package/src/migrations/latest/add-assemble-target/migration.d.ts +6 -0
- package/src/migrations/latest/add-assemble-target/migration.js +241 -0
- package/src/migrations/latest/add-assemble-target/migration.js.map +1 -0
- package/src/migrations/latest/declare-cacheable-target-inputs/metadata.json +3 -0
- package/src/migrations/latest/declare-cacheable-target-inputs/migration.d.ts +6 -0
- package/src/migrations/latest/declare-cacheable-target-inputs/migration.js +94 -0
- package/src/migrations/latest/declare-cacheable-target-inputs/migration.js.map +1 -0
- package/src/migrations/latest/dev-target-depends-on-pull-image/metadata.json +3 -0
- package/src/migrations/latest/dev-target-depends-on-pull-image/migration.d.ts +6 -0
- package/src/migrations/latest/dev-target-depends-on-pull-image/migration.js +67 -0
- package/src/migrations/latest/dev-target-depends-on-pull-image/migration.js.map +1 -0
- package/src/migrations/latest/docker-target-cache-soundness/metadata.json +3 -0
- package/src/migrations/latest/docker-target-cache-soundness/migration.d.ts +6 -0
- package/src/migrations/latest/docker-target-cache-soundness/migration.js +95 -0
- package/src/migrations/latest/docker-target-cache-soundness/migration.js.map +1 -0
- package/src/migrations/latest/open-api-codegen-executor/metadata.json +3 -0
- package/src/migrations/latest/open-api-codegen-executor/migration.d.ts +6 -0
- package/src/migrations/latest/open-api-codegen-executor/migration.js +100 -0
- package/src/migrations/latest/open-api-codegen-executor/migration.js.map +1 -0
- package/src/migrations/latest/python-test-input-scoping/metadata.json +3 -0
- package/src/migrations/latest/python-test-input-scoping/migration.d.ts +6 -0
- package/src/migrations/latest/python-test-input-scoping/migration.js +142 -0
- package/src/migrations/latest/python-test-input-scoping/migration.js.map +1 -0
- package/src/migrations/latest/terraform-fmt-check/metadata.json +3 -0
- package/src/migrations/latest/terraform-fmt-check/migration.d.ts +6 -0
- package/src/migrations/latest/terraform-fmt-check/migration.js +165 -0
- package/src/migrations/latest/terraform-fmt-check/migration.js.map +1 -0
- package/src/migrations/latest/terraform-provider-plugin-cache/metadata.json +3 -0
- package/src/migrations/latest/terraform-provider-plugin-cache/migration.d.ts +6 -0
- package/src/migrations/latest/terraform-provider-plugin-cache/migration.js +164 -0
- package/src/migrations/latest/terraform-provider-plugin-cache/migration.js.map +1 -0
- package/src/migrations/latest/vitest-coverage-output-dir/metadata.json +3 -0
- package/src/migrations/latest/vitest-coverage-output-dir/migration.d.ts +6 -0
- package/src/migrations/latest/vitest-coverage-output-dir/migration.js +177 -0
- package/src/migrations/latest/vitest-coverage-output-dir/migration.js.map +1 -0
- package/src/open-api/codegen/executor-schema.d.js +6 -0
- package/src/open-api/codegen/executor-schema.d.js.map +1 -0
- package/src/open-api/codegen/executor-schema.d.ts +18 -0
- package/src/open-api/codegen/executor-schema.json +33 -0
- package/src/open-api/codegen/executor.d.ts +18 -0
- package/src/open-api/codegen/executor.js +56 -0
- package/src/open-api/codegen/executor.js.map +1 -0
- package/src/preset/__snapshots__/generator.spec.ts.snap +2 -1
- package/src/py/agent/generator.js +21 -22
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/agent/react-connection/generator.js +9 -0
- package/src/py/agent/react-connection/generator.js.map +1 -1
- package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +0 -30
- package/src/py/fast-api/react/open-api.js +19 -7
- package/src/py/fast-api/react/open-api.js.map +1 -1
- package/src/py/mcp-server/generator.js +4 -4
- package/src/py/mcp-server/generator.js.map +1 -1
- package/src/py/project/generator.d.ts +9 -0
- package/src/py/project/generator.js +51 -1
- package/src/py/project/generator.js.map +1 -1
- package/src/py/rdb/generator.js +10 -4
- package/src/py/rdb/generator.js.map +1 -1
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +15 -0
- package/src/smithy/project/generator.js +10 -0
- package/src/smithy/project/generator.js.map +1 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +7 -0
- package/src/terraform/project/files/application/scripts/env.ts.template +11 -0
- package/src/terraform/project/files/application/scripts/init.ts.template +2 -1
- package/src/terraform/project/files/application/src/providers.tf.template +2 -2
- package/src/terraform/project/generator.d.ts +11 -1
- package/src/terraform/project/generator.js +104 -20
- package/src/terraform/project/generator.js.map +1 -1
- package/src/trpc/backend/generator.js +2 -2
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/ts/agent/generator.js +4 -7
- package/src/ts/agent/generator.js.map +1 -1
- package/src/ts/astro-docs/generator.js +5 -0
- package/src/ts/astro-docs/generator.js.map +1 -1
- package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +0 -30
- package/src/ts/dynamodb/generator.js +3 -0
- package/src/ts/dynamodb/generator.js.map +1 -1
- package/src/ts/lib/__snapshots__/generator.spec.ts.snap +10 -1
- package/src/ts/lib/__snapshots__/vitest.spec.ts.snap +1 -1
- package/src/ts/lib/generator.d.ts +16 -0
- package/src/ts/lib/generator.js +31 -5
- package/src/ts/lib/generator.js.map +1 -1
- package/src/ts/lib/vitest.d.ts +11 -0
- package/src/ts/lib/vitest.js +31 -2
- package/src/ts/lib/vitest.js.map +1 -1
- package/src/ts/mcp-server/generator.js +4 -7
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/rdb/generator.js +8 -5
- package/src/ts/rdb/generator.js.map +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +27 -11
- package/src/ts/react-website/app/generator.js +9 -1
- package/src/ts/react-website/app/generator.js.map +1 -1
- package/src/ts/sync/generator.js +19 -2
- package/src/ts/sync/generator.js.map +1 -1
- package/src/utils/agent-core-constructs/agent-core-constructs.js +3 -3
- package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
- package/src/utils/api-constructs/api-constructs.js +2 -2
- package/src/utils/api-constructs/api-constructs.js.map +1 -1
- package/src/utils/api-constructs/open-api-metadata.js +15 -36
- package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
- package/src/utils/api-constructs/trpc-operations-metadata.js +9 -3
- package/src/utils/api-constructs/trpc-operations-metadata.js.map +1 -1
- package/src/utils/bundle/bundle.js +13 -4
- package/src/utils/bundle/bundle.js.map +1 -1
- package/src/utils/connection/open-api/react.js +7 -17
- package/src/utils/connection/open-api/react.js.map +1 -1
- package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js +2 -2
- package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js.map +1 -1
- package/src/utils/docker.d.ts +22 -10
- package/src/utils/docker.js +27 -17
- package/src/utils/docker.js.map +1 -1
- package/src/utils/dynamodb-constructs/dynamodb-constructs.js +2 -2
- package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
- package/src/utils/files/common/scripts/src/dynamodb/start-container.ts.template +0 -15
- package/src/utils/files/common/scripts/src/rdb/shared/start-container.ts.template +0 -15
- 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/function-constructs/function-constructs.js +2 -2
- package/src/utils/function-constructs/function-constructs.js.map +1 -1
- package/src/utils/generators.d.ts +33 -0
- package/src/utils/generators.js +18 -3
- package/src/utils/generators.js.map +1 -1
- package/src/utils/init.js +4 -6
- package/src/utils/init.js.map +1 -1
- package/src/utils/mock-project-graph.js +5 -2
- package/src/utils/mock-project-graph.js.map +1 -1
- package/src/utils/nx.d.ts +19 -0
- package/src/utils/nx.js +23 -0
- package/src/utils/nx.js.map +1 -1
- package/src/utils/open-api-codegen-target.d.ts +28 -0
- package/src/utils/open-api-codegen-target.js +31 -0
- package/src/utils/open-api-codegen-target.js.map +1 -0
- package/src/utils/rdb-constructs/rdb-constructs.js +2 -2
- package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
- package/src/utils/website-constructs/website-constructs.js +2 -2
- package/src/utils/website-constructs/website-constructs.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/utils/connection/open-api/react.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type ProjectConfiguration,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { addTargetToLocalDev } from '../../../connection/local-dev.js';\nimport runtimeConfigGenerator from '../../../ts/react-website/runtime-config/generator.js';\nimport { addSingleImport, applyGritQL } from '../../ast.js';\nimport {\n type DependencyDeclaration,\n forDependencies,\n type MustDeclare,\n} from '../../declared-dependencies.js';\nimport { addDependenciesToPackageJson } from '../../dependencies.js';\nimport { updateGitIgnore } from '../../git.js';\nimport { kebabCase, toClassName } from '../../names.js';\nimport { sortObjectKeys } from '../../object.js';\nimport { type ITsDepVersion, withVersions } from '../../versions.js';\n\n/** Dependencies a caller must declare to add an OpenAPI React client. */\nexport const OPEN_API_REACT_DEPENDENCIES = [\n { name: 'oidc-client-ts' },\n { name: 'react-oidc-context' },\n { name: '@aws-sdk/credential-provider-cognito-identity' },\n { name: 'aws4fetch' },\n { name: '@tanstack/react-query' },\n { name: '@tanstack/react-query-devtools' },\n { name: '@smithy/types' },\n] as const satisfies readonly { name: ITsDepVersion }[];\n\nexport interface AddOpenApiReactClientOptions {\n /**\n * The react project to add the openapi client and build targets to\n */\n frontendProjectConfig: ProjectConfiguration;\n /**\n * The backend project which serves the api\n */\n backendProjectConfig: ProjectConfiguration;\n /**\n * The project which builds/generates the openapi spec\n */\n specBuildProject: ProjectConfiguration;\n /**\n * Name of the api\n */\n apiName: string;\n /**\n * Path to the openapi spec from the workspace root\n */\n specPath: string;\n /**\n * Fully qualified target name for the target that builds/generates the openapi spec\n */\n specBuildTargetName: string;\n /**\n * Authentication method\n */\n auth: 'iam' | 'cognito' | 'custom';\n /**\n * Port on which the backend project's local server listens\n */\n port: number;\n /**\n * When true, the generated provider reads from runtimeConfig.agentRuntimes\n * and converts Bedrock AgentCore Runtime ARNs to HTTP URLs.\n * When false (default), reads from runtimeConfig.apis directly.\n */\n isAgentRuntime?: boolean;\n /**\n * When set, the generated provider reads the gateway's URL from\n * runtimeConfig.gateways and routes to the agent via the gateway's\n * path-based `/<targetName>` route instead of invoking the runtime directly.\n */\n gatewayRoute?: {\n gatewayClassName: string;\n targetName: string;\n };\n /**\n * When true, skips the dev target setup. Use this when the caller\n * handles dev target configuration separately (e.g. agent connections\n * that use a component-specific dev target instead of 'serve').\n */\n skipLocalDev?: boolean;\n}\n\n/**\n * Adds an OpenAPI React client to the frontend project along with supporting build targets\n */\nexport const addOpenApiReactClient = async <\n const D extends DependencyDeclaration,\n>(\n tree: Tree,\n {\n apiName,\n frontendProjectConfig,\n backendProjectConfig,\n specBuildProject,\n specPath,\n specBuildTargetName,\n auth,\n port,\n isAgentRuntime = false,\n gatewayRoute,\n skipLocalDev = false,\n }: AddOpenApiReactClientOptions,\n declaration: D & MustDeclare<typeof OPEN_API_REACT_DEPENDENCIES, D>,\n) => {\n const clientGenTarget = `generate:${kebabCase(apiName)}-client`;\n const clientGenWatchTarget = `watch-${clientGenTarget}`;\n\n const generatedClientDir = joinPathFragments('generated', kebabCase(apiName));\n const generatedClientDirFromRoot = joinPathFragments(\n frontendProjectConfig.sourceRoot,\n generatedClientDir,\n );\n\n // Add TypeScript client generation to Frontend project.json\n updateProjectConfiguration(tree, frontendProjectConfig.name, {\n ...frontendProjectConfig,\n targets: sortObjectKeys({\n ...frontendProjectConfig.targets,\n // Generate should run before compile and bundle as the client is created as part of the website src\n ...Object.fromEntries(\n ['compile', 'bundle'].map((target) => [\n target,\n {\n ...frontendProjectConfig.targets?.[target],\n dependsOn: [\n ...(\n frontendProjectConfig.targets?.[target]?.dependsOn ?? []\n ).filter((t) => t !== clientGenTarget),\n clientGenTarget,\n ],\n },\n ]),\n ),\n [clientGenTarget]: {\n cache: true,\n executor: 'nx:run-commands',\n inputs: [\n {\n dependentTasksOutputFiles: '**/*.json',\n },\n ],\n outputs: [\n joinPathFragments('{workspaceRoot}', generatedClientDirFromRoot),\n ],\n options: {\n commands: [\n `nx g @aws/nx-plugin:open-api#ts-hooks --openApiSpecPath=\"${specPath}\" --outputPath=\"${generatedClientDirFromRoot}\" --no-interactive`,\n ],\n },\n dependsOn: [specBuildTargetName],\n },\n // Watch target for regenerating the client\n [clientGenWatchTarget]: {\n executor: 'nx:run-commands',\n options: {\n commands: [\n `nx watch --projects=${specBuildProject.name} --includeDependencies -- nx run ${frontendProjectConfig.name}:\"${clientGenTarget}\"`,\n ],\n },\n continuous: true,\n },\n }),\n });\n\n const relativeSrcDir = frontendProjectConfig.sourceRoot.slice(\n frontendProjectConfig.root.length + 1,\n );\n\n // Ignore the generated client by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(tree, frontendProjectConfig.root, (patterns) => [\n ...patterns,\n joinPathFragments(relativeSrcDir, generatedClientDir),\n ]);\n\n // Ensure that the frontend has runtime config as we'll use the url for creating the client\n await runtimeConfigGenerator(tree, {\n project: frontendProjectConfig.name,\n });\n\n // Add sigv4 fetch\n if (auth === 'iam') {\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, '../../files/website/hooks/sigv4'),\n joinPathFragments(frontendProjectConfig.sourceRoot, 'hooks'),\n {},\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n }\n\n // Generate the tanstack query provider if it does not exist already\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n '../../files/website/components/tanstack-query',\n ),\n joinPathFragments(frontendProjectConfig.sourceRoot, 'components'),\n {},\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n\n const apiNameClassName = toClassName(apiName);\n\n // Add a hook to instantiate the client\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files'),\n frontendProjectConfig.sourceRoot,\n {\n auth,\n apiName,\n apiNameClassName,\n generatedClientDir,\n isAgentRuntime,\n gatewayRoute,\n },\n );\n\n // Update main.tsx to add required providers\n const mainTsxPath = joinPathFragments(\n frontendProjectConfig.sourceRoot,\n 'main.tsx',\n );\n\n // Add the query client provider if it doesn't exist already\n await addSingleImport(\n tree,\n mainTsxPath,\n 'QueryClientProvider',\n './components/QueryClientProvider',\n );\n await applyGritQL(\n tree,\n mainTsxPath,\n '`<App />` => `<QueryClientProvider><App /></QueryClientProvider>` where { $program <: not contains `<QueryClientProvider>$_</QueryClientProvider>` }',\n );\n\n // Add the api provider if it does not exist\n const providerName = `${apiNameClassName}Provider`;\n await addSingleImport(\n tree,\n mainTsxPath,\n providerName,\n `./components/${providerName}`,\n );\n await applyGritQL(\n tree,\n mainTsxPath,\n `\\`<App />\\` => \\`<${providerName}><App /></${providerName}>\\` where { $program <: not contains \\`<${providerName}>$_</${providerName}>\\` }`,\n );\n\n // Update the dev target on the website to use our local server.\n // Callers that handle the dev target separately (e.g. agent connections) set skipLocalDev.\n if (!skipLocalDev) {\n await addTargetToLocalDev(\n tree,\n frontendProjectConfig.name,\n backendProjectConfig.name,\n {\n url: `http://localhost:${port}/`,\n apiName,\n // Additionally add a dependency on the generate watch command to ensure that local\n // API changes that affect the client are also reloaded.\n // We include a dependency on the generate target as watch ONLY triggers on a change,\n // and we need to ensure the client is generated the first time if not already present.\n additionalDependencyTargets: [clientGenTarget, clientGenWatchTarget],\n },\n );\n }\n\n addDependenciesToPackageJson(\n tree,\n withVersions(\n forDependencies<typeof OPEN_API_REACT_DEPENDENCIES>(declaration),\n [\n ...((auth === 'iam'\n ? [\n 'oidc-client-ts',\n 'react-oidc-context',\n '@aws-sdk/credential-provider-cognito-identity',\n 'aws4fetch',\n ]\n : []) as any),\n ...((auth === 'cognito' ? ['react-oidc-context'] : []) as any),\n '@tanstack/react-query',\n '@tanstack/react-query-devtools',\n ],\n ),\n withVersions(\n forDependencies<typeof OPEN_API_REACT_DEPENDENCIES>(declaration),\n ['@smithy/types'],\n ),\n joinPathFragments(frontendProjectConfig.root, 'package.json'),\n );\n};\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateProjectConfiguration","addTargetToLocalDev","runtimeConfigGenerator","addSingleImport","applyGritQL","forDependencies","addDependenciesToPackageJson","updateGitIgnore","kebabCase","toClassName","sortObjectKeys","withVersions","OPEN_API_REACT_DEPENDENCIES","name","addOpenApiReactClient","tree","apiName","frontendProjectConfig","backendProjectConfig","specBuildProject","specPath","specBuildTargetName","auth","port","isAgentRuntime","gatewayRoute","skipLocalDev","declaration","clientGenTarget","clientGenWatchTarget","generatedClientDir","generatedClientDirFromRoot","sourceRoot","targets","Object","fromEntries","map","target","dependsOn","filter","t","cache","executor","inputs","dependentTasksOutputFiles","outputs","options","commands","continuous","relativeSrcDir","slice","root","length","patterns","project","dirname","overwriteStrategy","KeepExisting","apiNameClassName","mainTsxPath","providerName","url","additionalDependencyTargets"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/utils/connection/open-api/react.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type ProjectConfiguration,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { addTargetToLocalDev } from '../../../connection/local-dev.js';\nimport runtimeConfigGenerator from '../../../ts/react-website/runtime-config/generator.js';\nimport { addSingleImport, applyGritQL } from '../../ast.js';\nimport {\n type DependencyDeclaration,\n forDependencies,\n type MustDeclare,\n} from '../../declared-dependencies.js';\nimport { addDependenciesToPackageJson } from '../../dependencies.js';\nimport { updateGitIgnore } from '../../git.js';\nimport { kebabCase, toClassName } from '../../names.js';\nimport { sortObjectKeys } from '../../object.js';\nimport { openApiCodegenTarget } from '../../open-api-codegen-target.js';\nimport { type ITsDepVersion, withVersions } from '../../versions.js';\n\n/** Dependencies a caller must declare to add an OpenAPI React client. */\nexport const OPEN_API_REACT_DEPENDENCIES = [\n { name: 'oidc-client-ts' },\n { name: 'react-oidc-context' },\n { name: '@aws-sdk/credential-provider-cognito-identity' },\n { name: 'aws4fetch' },\n { name: '@tanstack/react-query' },\n { name: '@tanstack/react-query-devtools' },\n { name: '@smithy/types' },\n] as const satisfies readonly { name: ITsDepVersion }[];\n\nexport interface AddOpenApiReactClientOptions {\n /**\n * The react project to add the openapi client and build targets to\n */\n frontendProjectConfig: ProjectConfiguration;\n /**\n * The backend project which serves the api\n */\n backendProjectConfig: ProjectConfiguration;\n /**\n * The project which builds/generates the openapi spec\n */\n specBuildProject: ProjectConfiguration;\n /**\n * Name of the api\n */\n apiName: string;\n /**\n * Path to the openapi spec from the workspace root\n */\n specPath: string;\n /**\n * Fully qualified target name for the target that builds/generates the openapi spec\n */\n specBuildTargetName: string;\n /**\n * Authentication method\n */\n auth: 'iam' | 'cognito' | 'custom';\n /**\n * Port on which the backend project's local server listens\n */\n port: number;\n /**\n * When true, the generated provider reads from runtimeConfig.agentRuntimes\n * and converts Bedrock AgentCore Runtime ARNs to HTTP URLs.\n * When false (default), reads from runtimeConfig.apis directly.\n */\n isAgentRuntime?: boolean;\n /**\n * When set, the generated provider reads the gateway's URL from\n * runtimeConfig.gateways and routes to the agent via the gateway's\n * path-based `/<targetName>` route instead of invoking the runtime directly.\n */\n gatewayRoute?: {\n gatewayClassName: string;\n targetName: string;\n };\n /**\n * When true, skips the dev target setup. Use this when the caller\n * handles dev target configuration separately (e.g. agent connections\n * that use a component-specific dev target instead of 'serve').\n */\n skipLocalDev?: boolean;\n}\n\n/**\n * Adds an OpenAPI React client to the frontend project along with supporting build targets\n */\nexport const addOpenApiReactClient = async <\n const D extends DependencyDeclaration,\n>(\n tree: Tree,\n {\n apiName,\n frontendProjectConfig,\n backendProjectConfig,\n specBuildProject,\n specPath,\n specBuildTargetName,\n auth,\n port,\n isAgentRuntime = false,\n gatewayRoute,\n skipLocalDev = false,\n }: AddOpenApiReactClientOptions,\n declaration: D & MustDeclare<typeof OPEN_API_REACT_DEPENDENCIES, D>,\n) => {\n const clientGenTarget = `generate:${kebabCase(apiName)}-client`;\n const clientGenWatchTarget = `watch-${clientGenTarget}`;\n\n const generatedClientDir = joinPathFragments('generated', kebabCase(apiName));\n const generatedClientDirFromRoot = joinPathFragments(\n frontendProjectConfig.sourceRoot,\n generatedClientDir,\n );\n\n // Add TypeScript client generation to Frontend project.json\n updateProjectConfiguration(tree, frontendProjectConfig.name, {\n ...frontendProjectConfig,\n targets: sortObjectKeys({\n ...frontendProjectConfig.targets,\n // Generate should run before compile and bundle as the client is created as part of the website src\n ...Object.fromEntries(\n ['compile', 'bundle'].map((target) => [\n target,\n {\n ...frontendProjectConfig.targets?.[target],\n dependsOn: [\n ...(\n frontendProjectConfig.targets?.[target]?.dependsOn ?? []\n ).filter((t) => t !== clientGenTarget),\n clientGenTarget,\n ],\n },\n ]),\n ),\n [clientGenTarget]: openApiCodegenTarget({\n generator: 'ts-hooks',\n openApiSpecPath: specPath,\n outputPath: generatedClientDirFromRoot,\n specBuildTargetName,\n outputs: [\n joinPathFragments('{workspaceRoot}', generatedClientDirFromRoot),\n ],\n }),\n // Watch target for regenerating the client\n [clientGenWatchTarget]: {\n executor: 'nx:run-commands',\n options: {\n commands: [\n `nx watch --projects=${specBuildProject.name} --includeDependencies -- nx run ${frontendProjectConfig.name}:\"${clientGenTarget}\"`,\n ],\n },\n continuous: true,\n },\n }),\n });\n\n const relativeSrcDir = frontendProjectConfig.sourceRoot.slice(\n frontendProjectConfig.root.length + 1,\n );\n\n // Ignore the generated client by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(tree, frontendProjectConfig.root, (patterns) => [\n ...patterns,\n joinPathFragments(relativeSrcDir, generatedClientDir),\n ]);\n\n // Ensure that the frontend has runtime config as we'll use the url for creating the client\n await runtimeConfigGenerator(tree, {\n project: frontendProjectConfig.name,\n });\n\n // Add sigv4 fetch\n if (auth === 'iam') {\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, '../../files/website/hooks/sigv4'),\n joinPathFragments(frontendProjectConfig.sourceRoot, 'hooks'),\n {},\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n }\n\n // Generate the tanstack query provider if it does not exist already\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n '../../files/website/components/tanstack-query',\n ),\n joinPathFragments(frontendProjectConfig.sourceRoot, 'components'),\n {},\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n\n const apiNameClassName = toClassName(apiName);\n\n // Add a hook to instantiate the client\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files'),\n frontendProjectConfig.sourceRoot,\n {\n auth,\n apiName,\n apiNameClassName,\n generatedClientDir,\n isAgentRuntime,\n gatewayRoute,\n },\n );\n\n // Update main.tsx to add required providers\n const mainTsxPath = joinPathFragments(\n frontendProjectConfig.sourceRoot,\n 'main.tsx',\n );\n\n // Add the query client provider if it doesn't exist already\n await addSingleImport(\n tree,\n mainTsxPath,\n 'QueryClientProvider',\n './components/QueryClientProvider',\n );\n await applyGritQL(\n tree,\n mainTsxPath,\n '`<App />` => `<QueryClientProvider><App /></QueryClientProvider>` where { $program <: not contains `<QueryClientProvider>$_</QueryClientProvider>` }',\n );\n\n // Add the api provider if it does not exist\n const providerName = `${apiNameClassName}Provider`;\n await addSingleImport(\n tree,\n mainTsxPath,\n providerName,\n `./components/${providerName}`,\n );\n await applyGritQL(\n tree,\n mainTsxPath,\n `\\`<App />\\` => \\`<${providerName}><App /></${providerName}>\\` where { $program <: not contains \\`<${providerName}>$_</${providerName}>\\` }`,\n );\n\n // Update the dev target on the website to use our local server.\n // Callers that handle the dev target separately (e.g. agent connections) set skipLocalDev.\n if (!skipLocalDev) {\n await addTargetToLocalDev(\n tree,\n frontendProjectConfig.name,\n backendProjectConfig.name,\n {\n url: `http://localhost:${port}/`,\n apiName,\n // Additionally add a dependency on the generate watch command to ensure that local\n // API changes that affect the client are also reloaded.\n // We include a dependency on the generate target as watch ONLY triggers on a change,\n // and we need to ensure the client is generated the first time if not already present.\n additionalDependencyTargets: [clientGenTarget, clientGenWatchTarget],\n },\n );\n }\n\n addDependenciesToPackageJson(\n tree,\n withVersions(\n forDependencies<typeof OPEN_API_REACT_DEPENDENCIES>(declaration),\n [\n ...((auth === 'iam'\n ? [\n 'oidc-client-ts',\n 'react-oidc-context',\n '@aws-sdk/credential-provider-cognito-identity',\n 'aws4fetch',\n ]\n : []) as any),\n ...((auth === 'cognito' ? ['react-oidc-context'] : []) as any),\n '@tanstack/react-query',\n '@tanstack/react-query-devtools',\n ],\n ),\n withVersions(\n forDependencies<typeof OPEN_API_REACT_DEPENDENCIES>(declaration),\n ['@smithy/types'],\n ),\n joinPathFragments(frontendProjectConfig.root, 'package.json'),\n );\n};\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateProjectConfiguration","addTargetToLocalDev","runtimeConfigGenerator","addSingleImport","applyGritQL","forDependencies","addDependenciesToPackageJson","updateGitIgnore","kebabCase","toClassName","sortObjectKeys","openApiCodegenTarget","withVersions","OPEN_API_REACT_DEPENDENCIES","name","addOpenApiReactClient","tree","apiName","frontendProjectConfig","backendProjectConfig","specBuildProject","specPath","specBuildTargetName","auth","port","isAgentRuntime","gatewayRoute","skipLocalDev","declaration","clientGenTarget","clientGenWatchTarget","generatedClientDir","generatedClientDirFromRoot","sourceRoot","targets","Object","fromEntries","map","target","dependsOn","filter","t","generator","openApiSpecPath","outputPath","outputs","executor","options","commands","continuous","relativeSrcDir","slice","root","length","patterns","project","dirname","overwriteStrategy","KeepExisting","apiNameClassName","mainTsxPath","providerName","url","additionalDependencyTargets"],"mappings":"AAAA;;;CAGC,GACD,SACEA,aAAa,EACbC,iBAAiB,EACjBC,iBAAiB,EAGjBC,0BAA0B,QACrB,aAAa;AACpB,SAASC,mBAAmB,QAAQ,mCAAmC;AACvE,OAAOC,4BAA4B,wDAAwD;AAC3F,SAASC,eAAe,EAAEC,WAAW,QAAQ,eAAe;AAC5D,SAEEC,eAAe,QAEV,iCAAiC;AACxC,SAASC,4BAA4B,QAAQ,wBAAwB;AACrE,SAASC,eAAe,QAAQ,eAAe;AAC/C,SAASC,SAAS,EAAEC,WAAW,QAAQ,iBAAiB;AACxD,SAASC,cAAc,QAAQ,kBAAkB;AACjD,SAASC,oBAAoB,QAAQ,mCAAmC;AACxE,SAA6BC,YAAY,QAAQ,oBAAoB;AAErE,uEAAuE,GACvE,OAAO,MAAMC,8BAA8B;IACzC;QAAEC,MAAM;IAAiB;IACzB;QAAEA,MAAM;IAAqB;IAC7B;QAAEA,MAAM;IAAgD;IACxD;QAAEA,MAAM;IAAY;IACpB;QAAEA,MAAM;IAAwB;IAChC;QAAEA,MAAM;IAAiC;IACzC;QAAEA,MAAM;IAAgB;CACzB,CAAuD;AA0DxD;;CAEC,GACD,OAAO,MAAMC,wBAAwB,OAGnCC,MACA,EACEC,OAAO,EACPC,qBAAqB,EACrBC,oBAAoB,EACpBC,gBAAgB,EAChBC,QAAQ,EACRC,mBAAmB,EACnBC,IAAI,EACJC,IAAI,EACJC,iBAAiB,KAAK,EACtBC,YAAY,EACZC,eAAe,KAAK,EACS,EAC/BC;IAEA,MAAMC,kBAAkB,CAAC,SAAS,EAAErB,UAAUS,SAAS,OAAO,CAAC;IAC/D,MAAMa,uBAAuB,CAAC,MAAM,EAAED,iBAAiB;IAEvD,MAAME,qBAAqBjC,kBAAkB,aAAaU,UAAUS;IACpE,MAAMe,6BAA6BlC,kBACjCoB,sBAAsBe,UAAU,EAChCF;IAGF,4DAA4D;IAC5D/B,2BAA2BgB,MAAME,sBAAsBJ,IAAI,EAAE;QAC3D,GAAGI,qBAAqB;QACxBgB,SAASxB,eAAe;YACtB,GAAGQ,sBAAsBgB,OAAO;YAChC,oGAAoG;YACpG,GAAGC,OAAOC,WAAW,CACnB;gBAAC;gBAAW;aAAS,CAACC,GAAG,CAAC,CAACC,SAAW;oBACpCA;oBACA;wBACE,GAAGpB,sBAAsBgB,OAAO,EAAE,CAACI,OAAO;wBAC1CC,WAAW;+BACN,AACDrB,CAAAA,sBAAsBgB,OAAO,EAAE,CAACI,OAAO,EAAEC,aAAa,EAAE,AAAD,EACvDC,MAAM,CAAC,CAACC,IAAMA,MAAMZ;4BACtBA;yBACD;oBACH;iBACD,EACF;YACD,CAACA,gBAAgB,EAAElB,qBAAqB;gBACtC+B,WAAW;gBACXC,iBAAiBtB;gBACjBuB,YAAYZ;gBACZV;gBACAuB,SAAS;oBACP/C,kBAAkB,mBAAmBkC;iBACtC;YACH;YACA,2CAA2C;YAC3C,CAACF,qBAAqB,EAAE;gBACtBgB,UAAU;gBACVC,SAAS;oBACPC,UAAU;wBACR,CAAC,oBAAoB,EAAE5B,iBAAiBN,IAAI,CAAC,iCAAiC,EAAEI,sBAAsBJ,IAAI,CAAC,EAAE,EAAEe,gBAAgB,CAAC,CAAC;qBAClI;gBACH;gBACAoB,YAAY;YACd;QACF;IACF;IAEA,MAAMC,iBAAiBhC,sBAAsBe,UAAU,CAACkB,KAAK,CAC3DjC,sBAAsBkC,IAAI,CAACC,MAAM,GAAG;IAGtC,yCAAyC;IACzC,sFAAsF;IACtF9C,gBAAgBS,MAAME,sBAAsBkC,IAAI,EAAE,CAACE,WAAa;eAC3DA;YACHxD,kBAAkBoD,gBAAgBnB;SACnC;IAED,2FAA2F;IAC3F,MAAM7B,uBAAuBc,MAAM;QACjCuC,SAASrC,sBAAsBJ,IAAI;IACrC;IAEA,kBAAkB;IAClB,IAAIS,SAAS,OAAO;QAClB1B,cACEmB,MACAlB,kBAAkB,YAAY0D,OAAO,EAAE,oCACvC1D,kBAAkBoB,sBAAsBe,UAAU,EAAE,UACpD,CAAC,GACD;YACEwB,mBAAmB1D,kBAAkB2D,YAAY;QACnD;IAEJ;IAEA,oEAAoE;IACpE7D,cACEmB,MACAlB,kBACE,YAAY0D,OAAO,EACnB,kDAEF1D,kBAAkBoB,sBAAsBe,UAAU,EAAE,eACpD,CAAC,GACD;QACEwB,mBAAmB1D,kBAAkB2D,YAAY;IACnD;IAGF,MAAMC,mBAAmBlD,YAAYQ;IAErC,uCAAuC;IACvCpB,cACEmB,MACAlB,kBAAkB,YAAY0D,OAAO,EAAE,UACvCtC,sBAAsBe,UAAU,EAChC;QACEV;QACAN;QACA0C;QACA5B;QACAN;QACAC;IACF;IAGF,4CAA4C;IAC5C,MAAMkC,cAAc9D,kBAClBoB,sBAAsBe,UAAU,EAChC;IAGF,4DAA4D;IAC5D,MAAM9B,gBACJa,MACA4C,aACA,uBACA;IAEF,MAAMxD,YACJY,MACA4C,aACA;IAGF,4CAA4C;IAC5C,MAAMC,eAAe,GAAGF,iBAAiB,QAAQ,CAAC;IAClD,MAAMxD,gBACJa,MACA4C,aACAC,cACA,CAAC,aAAa,EAAEA,cAAc;IAEhC,MAAMzD,YACJY,MACA4C,aACA,CAAC,kBAAkB,EAAEC,aAAa,UAAU,EAAEA,aAAa,wCAAwC,EAAEA,aAAa,KAAK,EAAEA,aAAa,KAAK,CAAC;IAG9I,gEAAgE;IAChE,2FAA2F;IAC3F,IAAI,CAAClC,cAAc;QACjB,MAAM1B,oBACJe,MACAE,sBAAsBJ,IAAI,EAC1BK,qBAAqBL,IAAI,EACzB;YACEgD,KAAK,CAAC,iBAAiB,EAAEtC,KAAK,CAAC,CAAC;YAChCP;YACA,mFAAmF;YACnF,wDAAwD;YACxD,qFAAqF;YACrF,uFAAuF;YACvF8C,6BAA6B;gBAAClC;gBAAiBC;aAAqB;QACtE;IAEJ;IAEAxB,6BACEU,MACAJ,aACEP,gBAAoDuB,cACpD;WACOL,SAAS,QACV;YACE;YACA;YACA;YACA;SACD,GACD,EAAE;WACDA,SAAS,YAAY;YAAC;SAAqB,GAAG,EAAE;QACrD;QACA;KACD,GAEHX,aACEP,gBAAoDuB,cACpD;QAAC;KAAgB,GAEnB9B,kBAAkBoB,sBAAsBkC,IAAI,EAAE;AAElD,EAAE"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/ import { generateFiles, joinPathFragments, OverwriteStrategy, updateJson } from "@nx/devkit";
|
|
5
5
|
import { addStarExport } from "../ast.js";
|
|
6
6
|
import { esmVars } from "../module-format.js";
|
|
7
|
-
import {
|
|
7
|
+
import { addArtifactProjectToTargets } from "../nx.js";
|
|
8
8
|
import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, SHARED_TERRAFORM_DIR } from "../shared-constructs-constants.js";
|
|
9
9
|
import { cdkLambdaRuntimeVars, terraformLambdaRuntimeVars, terraformProviderVersions } from "../versions.js";
|
|
10
10
|
/**
|
|
@@ -37,7 +37,7 @@ import { cdkLambdaRuntimeVars, terraformLambdaRuntimeVars, terraformProviderVers
|
|
|
37
37
|
}
|
|
38
38
|
// Ensure the handler bundles are built before the shared package
|
|
39
39
|
updateJson(tree, joinPathFragments(PACKAGES_DIR, options.iac === 'cdk' ? SHARED_CONSTRUCTS_DIR : SHARED_TERRAFORM_DIR, 'project.json'), (config)=>{
|
|
40
|
-
|
|
40
|
+
addArtifactProjectToTargets(config, options.proxyProjectName);
|
|
41
41
|
return config;
|
|
42
42
|
});
|
|
43
43
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { addStarExport } from '../ast.js';\nimport type { Iac } from '../iac.js';\nimport { esmVars } from '../module-format.js';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { addStarExport } from '../ast.js';\nimport type { Iac } from '../iac.js';\nimport { esmVars } from '../module-format.js';\nimport { addArtifactProjectToTargets } from '../nx.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../shared-constructs-constants.js';\nimport {\n cdkLambdaRuntimeVars,\n terraformLambdaRuntimeVars,\n terraformProviderVersions,\n} from '../versions.js';\n\n/**\n * The DCR proxy Lambda handlers. Each is bundled independently and wired to a\n * dedicated route on the HTTP API.\n */\nexport const DCR_PROXY_HANDLERS = [\n 'authorization-server-metadata',\n 'authorize',\n 'mcp-proxy',\n 'protected-resource-metadata',\n 'register',\n 'token',\n] as const;\n\nexport type DcrProxyHandler = (typeof DCR_PROXY_HANDLERS)[number];\n\nexport interface AddDcrProxyInfraProps {\n dcrProxyNameClassName: string;\n dcrProxyNameKebabCase: string;\n\n /**\n * Fully qualified name of the TypeScript project that owns the bundled\n * handlers, used to wire the shared package build to depend on it.\n */\n proxyProjectName: string;\n\n /**\n * Map of handler name to the workspace-root-relative path of its bundle\n * output directory (e.g. `dist/packages/dcr-proxy/bundle/token`).\n */\n bundlePathsFromRoot: Record<DcrProxyHandler, string>;\n\n iac: Iac;\n}\n\n/**\n * Add a DCR proxy infrastructure (CDK construct or Terraform module) to the\n * relevant shared package.\n *\n * The infrastructure exposes props/variables for the caller to inject their\n * Cognito identifiers and the upstream MCP server URL from their own stack,\n * since these may or may not be generated by this plugin. The Lambda handlers\n * live in a standalone TypeScript project and are bundled by rolldown; both\n * IaC providers reference the same bundle output.\n */\nexport const addDcrProxyInfra = async (\n tree: Tree,\n options: AddDcrProxyInfraProps,\n) => {\n if (options.iac === 'cdk') {\n await addDcrProxyCdkConstructs(tree, options);\n } else if (options.iac === 'terraform') {\n addDcrProxyTerraformModules(tree, options);\n } else {\n throw new Error(`Unsupported iac ${options.iac}`);\n }\n\n // Ensure the handler bundles are built before the shared package\n updateJson(\n tree,\n joinPathFragments(\n PACKAGES_DIR,\n options.iac === 'cdk' ? SHARED_CONSTRUCTS_DIR : SHARED_TERRAFORM_DIR,\n 'project.json',\n ),\n (config: ProjectConfiguration) => {\n addArtifactProjectToTargets(config, options.proxyProjectName);\n return config;\n },\n );\n};\n\n/**\n * Add the DCR proxy CDK construct to the shared constructs package.\n */\nconst addDcrProxyCdkConstructs = async (\n tree: Tree,\n options: AddDcrProxyInfraProps,\n) => {\n // Ensure the core HTTP API construct + utils are available (the proxy reuses\n // them). These are shared with the api generators and safe to (re)generate\n // with KeepExisting.\n for (const coreApiFile of ['http', 'utils']) {\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n '..',\n 'api-constructs',\n 'files',\n 'cdk',\n 'core',\n 'api',\n coreApiFile,\n ),\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'core',\n 'api',\n ),\n { ...esmVars(tree) },\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n }\n\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n 'files',\n 'cdk',\n 'app',\n 'dcr-proxies',\n ),\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'app',\n 'dcr-proxies',\n ),\n {\n nameClassName: options.dcrProxyNameClassName,\n nameKebabCase: options.dcrProxyNameKebabCase,\n bundlePathsFromRoot: options.bundlePathsFromRoot,\n ...esmVars(tree),\n ...cdkLambdaRuntimeVars(),\n },\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n\n // Export the construct\n await addStarExport(\n tree,\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'app',\n 'dcr-proxies',\n 'index.ts',\n ),\n `./${options.dcrProxyNameKebabCase}/${options.dcrProxyNameKebabCase}.js`,\n );\n await addStarExport(\n tree,\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'app',\n 'index.ts',\n ),\n './dcr-proxies/index.js',\n );\n};\n\n/**\n * Add the DCR proxy Terraform module to the shared terraform package.\n */\nconst addDcrProxyTerraformModules = (\n tree: Tree,\n options: AddDcrProxyInfraProps,\n) => {\n // Ensure the core HTTP API module is available (the proxy reuses it). This is\n // shared with the api generators and safe to (re)generate with KeepExisting.\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n '..',\n 'api-constructs',\n 'files',\n 'terraform',\n 'core',\n 'api',\n 'http',\n ),\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'src', 'core', 'api'),\n { ...terraformProviderVersions() },\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n\n // Generate the app specific terraform module\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n 'files',\n 'terraform',\n 'app',\n 'dcr-proxies',\n ),\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n 'app',\n 'dcr-proxies',\n ),\n {\n nameClassName: options.dcrProxyNameClassName,\n nameKebabCase: options.dcrProxyNameKebabCase,\n bundlePathsFromRoot: options.bundlePathsFromRoot,\n ...terraformProviderVersions(),\n ...terraformLambdaRuntimeVars(),\n },\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n};\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateJson","addStarExport","esmVars","addArtifactProjectToTargets","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","cdkLambdaRuntimeVars","terraformLambdaRuntimeVars","terraformProviderVersions","DCR_PROXY_HANDLERS","addDcrProxyInfra","tree","options","iac","addDcrProxyCdkConstructs","addDcrProxyTerraformModules","Error","config","proxyProjectName","coreApiFile","dirname","overwriteStrategy","KeepExisting","nameClassName","dcrProxyNameClassName","nameKebabCase","dcrProxyNameKebabCase","bundlePathsFromRoot"],"mappings":"AAAA;;;CAGC,GACD,SACEA,aAAa,EACbC,iBAAiB,EACjBC,iBAAiB,EAGjBC,UAAU,QACL,aAAa;AACpB,SAASC,aAAa,QAAQ,YAAY;AAE1C,SAASC,OAAO,QAAQ,sBAAsB;AAC9C,SAASC,2BAA2B,QAAQ,WAAW;AACvD,SACEC,YAAY,EACZC,qBAAqB,EACrBC,oBAAoB,QACf,oCAAoC;AAC3C,SACEC,oBAAoB,EACpBC,0BAA0B,EAC1BC,yBAAyB,QACpB,iBAAiB;AAExB;;;CAGC,GACD,OAAO,MAAMC,qBAAqB;IAChC;IACA;IACA;IACA;IACA;IACA;CACD,CAAU;AAuBX;;;;;;;;;CASC,GACD,OAAO,MAAMC,mBAAmB,OAC9BC,MACAC;IAEA,IAAIA,QAAQC,GAAG,KAAK,OAAO;QACzB,MAAMC,yBAAyBH,MAAMC;IACvC,OAAO,IAAIA,QAAQC,GAAG,KAAK,aAAa;QACtCE,4BAA4BJ,MAAMC;IACpC,OAAO;QACL,MAAM,IAAII,MAAM,CAAC,gBAAgB,EAAEJ,QAAQC,GAAG,EAAE;IAClD;IAEA,iEAAiE;IACjEd,WACEY,MACAd,kBACEM,cACAS,QAAQC,GAAG,KAAK,QAAQT,wBAAwBC,sBAChD,iBAEF,CAACY;QACCf,4BAA4Be,QAAQL,QAAQM,gBAAgB;QAC5D,OAAOD;IACT;AAEJ,EAAE;AAEF;;CAEC,GACD,MAAMH,2BAA2B,OAC/BH,MACAC;IAEA,6EAA6E;IAC7E,2EAA2E;IAC3E,qBAAqB;IACrB,KAAK,MAAMO,eAAe;QAAC;QAAQ;KAAQ,CAAE;QAC3CvB,cACEe,MACAd,kBACE,YAAYuB,OAAO,EACnB,MACA,kBACA,SACA,OACA,QACA,OACAD,cAEFtB,kBACEM,cACAC,uBACA,OACA,QACA,QAEF;YAAE,GAAGH,QAAQU,KAAK;QAAC,GACnB;YACEU,mBAAmBvB,kBAAkBwB,YAAY;QACnD;IAEJ;IAEA1B,cACEe,MACAd,kBACE,YAAYuB,OAAO,EACnB,SACA,OACA,OACA,gBAEFvB,kBACEM,cACAC,uBACA,OACA,OACA,gBAEF;QACEmB,eAAeX,QAAQY,qBAAqB;QAC5CC,eAAeb,QAAQc,qBAAqB;QAC5CC,qBAAqBf,QAAQe,mBAAmB;QAChD,GAAG1B,QAAQU,KAAK;QAChB,GAAGL,sBAAsB;IAC3B,GACA;QACEe,mBAAmBvB,kBAAkBwB,YAAY;IACnD;IAGF,uBAAuB;IACvB,MAAMtB,cACJW,MACAd,kBACEM,cACAC,uBACA,OACA,OACA,eACA,aAEF,CAAC,EAAE,EAAEQ,QAAQc,qBAAqB,CAAC,CAAC,EAAEd,QAAQc,qBAAqB,CAAC,GAAG,CAAC;IAE1E,MAAM1B,cACJW,MACAd,kBACEM,cACAC,uBACA,OACA,OACA,aAEF;AAEJ;AAEA;;CAEC,GACD,MAAMW,8BAA8B,CAClCJ,MACAC;IAEA,8EAA8E;IAC9E,6EAA6E;IAC7EhB,cACEe,MACAd,kBACE,YAAYuB,OAAO,EACnB,MACA,kBACA,SACA,aACA,QACA,OACA,SAEFvB,kBAAkBM,cAAcE,sBAAsB,OAAO,QAAQ,QACrE;QAAE,GAAGG,2BAA2B;IAAC,GACjC;QACEa,mBAAmBvB,kBAAkBwB,YAAY;IACnD;IAGF,6CAA6C;IAC7C1B,cACEe,MACAd,kBACE,YAAYuB,OAAO,EACnB,SACA,aACA,OACA,gBAEFvB,kBACEM,cACAE,sBACA,OACA,OACA,gBAEF;QACEkB,eAAeX,QAAQY,qBAAqB;QAC5CC,eAAeb,QAAQc,qBAAqB;QAC5CC,qBAAqBf,QAAQe,mBAAmB;QAChD,GAAGnB,2BAA2B;QAC9B,GAAGD,4BAA4B;IACjC,GACA;QACEc,mBAAmBvB,kBAAkBwB,YAAY;IACnD;AAEJ"}
|
package/src/utils/docker.d.ts
CHANGED
|
@@ -14,6 +14,18 @@ export declare const DOCKER_DEPENDENCIES: readonly [{
|
|
|
14
14
|
}, {
|
|
15
15
|
readonly name: "cpy-cli";
|
|
16
16
|
}];
|
|
17
|
+
/**
|
|
18
|
+
* The `cache` setting every target that builds a container image must use.
|
|
19
|
+
*
|
|
20
|
+
* A built image lives in the container engine's own store, not under any
|
|
21
|
+
* `outputs` path, so Nx has nothing to restore a cache hit from: it would
|
|
22
|
+
* report the build complete while the image is missing, and the `trivy` scan
|
|
23
|
+
* that depends on it would report a cached pass against an image that isn't
|
|
24
|
+
* there. The container engine's layer cache is the sound cache here — it is
|
|
25
|
+
* keyed on the build context and lives alongside the image, so an unchanged
|
|
26
|
+
* context rebuilds in well under a second and always leaves the image present.
|
|
27
|
+
*/
|
|
28
|
+
export declare const IMAGE_BUILD_CACHE = false;
|
|
17
29
|
/**
|
|
18
30
|
* Substitution variables for the vended Node `Dockerfile` templates: the npm
|
|
19
31
|
* version to install globally, and the versions its dependency overrides pin.
|
|
@@ -71,17 +83,17 @@ export interface DockerScanTargetOptions {
|
|
|
71
83
|
readonly imageTags: string[];
|
|
72
84
|
}
|
|
73
85
|
/**
|
|
74
|
-
* Add a
|
|
75
|
-
*
|
|
76
|
-
* \`
|
|
86
|
+
* Add a Trivy scan target for the images built by a docker target and wire it
|
|
87
|
+
* under the aggregate \`trivy\` target. The scan is not part of \`build\` — run
|
|
88
|
+
* \`nx run-many --target trivy\` (the root \`trivy\` script) in CI.
|
|
77
89
|
*
|
|
78
|
-
* The scan target depends on the docker target and
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
90
|
+
* The scan target depends on the docker target and is not cacheable, because
|
|
91
|
+
* what it scans lives in the container engine rather than under its \`outputs\`.
|
|
92
|
+
* Each image is saved to a tarball under \`dist/<projectRoot>/trivy/<scan-key>\`
|
|
93
|
+
* (kept out of any Docker build context) and scanned with the pinned ECR-hosted
|
|
94
|
+
* Trivy image via a workspace-relative bind mount, so the same commands work
|
|
95
|
+
* under both docker and finch. The scan exits non-zero (exit code 1) on
|
|
96
|
+
* HIGH/CRITICAL vulnerabilities.
|
|
85
97
|
*
|
|
86
98
|
* A \`.trivyignore\` is vended at the project root (kept if it already exists)
|
|
87
99
|
* for suppressing findings.
|
package/src/utils/docker.js
CHANGED
|
@@ -9,6 +9,17 @@ import { containerImage, TS_VERSIONS } from "./versions.js";
|
|
|
9
9
|
/** Dependencies a caller must declare to add a Docker scan target. */ export const DOCKER_DEPENDENCIES = [
|
|
10
10
|
...FS_DEPENDENCIES
|
|
11
11
|
];
|
|
12
|
+
/**
|
|
13
|
+
* The `cache` setting every target that builds a container image must use.
|
|
14
|
+
*
|
|
15
|
+
* A built image lives in the container engine's own store, not under any
|
|
16
|
+
* `outputs` path, so Nx has nothing to restore a cache hit from: it would
|
|
17
|
+
* report the build complete while the image is missing, and the `trivy` scan
|
|
18
|
+
* that depends on it would report a cached pass against an image that isn't
|
|
19
|
+
* there. The container engine's layer cache is the sound cache here — it is
|
|
20
|
+
* keyed on the build context and lives alongside the image, so an unchanged
|
|
21
|
+
* context rebuilds in well under a second and always leaves the image present.
|
|
22
|
+
*/ export const IMAGE_BUILD_CACHE = false;
|
|
12
23
|
const TRIVY_IGNORE_FILE = '.trivyignore';
|
|
13
24
|
const TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.g. CVE-2021-12345) per line to
|
|
14
25
|
# suppress it during the image scan (\`nx run-many --target trivy\`).
|
|
@@ -51,17 +62,17 @@ const TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.
|
|
|
51
62
|
}
|
|
52
63
|
];
|
|
53
64
|
/**
|
|
54
|
-
* Add a
|
|
55
|
-
*
|
|
56
|
-
* \`
|
|
65
|
+
* Add a Trivy scan target for the images built by a docker target and wire it
|
|
66
|
+
* under the aggregate \`trivy\` target. The scan is not part of \`build\` — run
|
|
67
|
+
* \`nx run-many --target trivy\` (the root \`trivy\` script) in CI.
|
|
57
68
|
*
|
|
58
|
-
* The scan target depends on the docker target and
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
69
|
+
* The scan target depends on the docker target and is not cacheable, because
|
|
70
|
+
* what it scans lives in the container engine rather than under its \`outputs\`.
|
|
71
|
+
* Each image is saved to a tarball under \`dist/<projectRoot>/trivy/<scan-key>\`
|
|
72
|
+
* (kept out of any Docker build context) and scanned with the pinned ECR-hosted
|
|
73
|
+
* Trivy image via a workspace-relative bind mount, so the same commands work
|
|
74
|
+
* under both docker and finch. The scan exits non-zero (exit code 1) on
|
|
75
|
+
* HIGH/CRITICAL vulnerabilities.
|
|
65
76
|
*
|
|
66
77
|
* A \`.trivyignore\` is vended at the project root (kept if it already exists)
|
|
67
78
|
* for suppressing findings.
|
|
@@ -92,13 +103,12 @@ const TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.
|
|
|
92
103
|
});
|
|
93
104
|
project.targets ??= {};
|
|
94
105
|
project.targets[trivyTargetName] = {
|
|
95
|
-
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
inputs
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
],
|
|
106
|
+
// The image being scanned is only reachable through the container engine,
|
|
107
|
+
// so a restored cache entry would report a pass for an image that may no
|
|
108
|
+
// longer exist. The scan re-runs and either scans the real image or fails.
|
|
109
|
+
// Nx only hashes a cacheable task, so declaring `inputs` here would have no
|
|
110
|
+
// effect.
|
|
111
|
+
cache: false,
|
|
102
112
|
outputs: [
|
|
103
113
|
`{workspaceRoot}/${scanDir}`
|
|
104
114
|
],
|
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\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 cacheable Trivy scan target for the images built by a docker target\n * and wire it under the aggregate \\`trivy\\` target. The scan is not part of\n * \\`build\\` — run \\`nx run-many --target trivy\\` (the root \\`trivy\\` script) in CI.\n *\n * The scan target depends on the docker target and declares the project source\n * as its inputs, so an unchanged image is never re-scanned (a cache hit skips\n * the scan entirely). Each image is saved to a tarball under\n * \\`dist/<projectRoot>/trivy/<scan-key>\\` (kept out of any Docker build context)\n * and scanned with the pinned ECR-hosted Trivy image via a workspace-relative\n * bind mount, so the same commands work under both docker and finch. The scan\n * exits non-zero (exit code 1) on 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 cache: true,\n // The scanned image is fully determined by the project source, so caching\n // on the source ensures an unchanged image is never re-scanned.\n inputs: ['default', '^production'],\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","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","inputs","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,MAAMM,oBAAoB;AAE1B,MAAMC,wBAAwB,CAAC;;;AAG/B,CAAC;AAED;;;CAGC,GACD,OAAO,MAAMC,oBAAoB,IAAO,CAAA;QACtCC,YAAYL,YAAYM,GAAG;QAC3BC,kBAAkBP,YAAYQ,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,iBAAiB7B,kBAAkB2B,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,UAAUlC,kBAAkB,QAAQ2B,aAAa,SAASK;IAChE,MAAMG,aAAa9B,eAAe;IAElC,MAAM+B,KAAK,IAAIjC,WACbgB,MACAlB,gBAA4CoB;IAE9C,MAAMgB,WAAW;QACfD,GAAGE,EAAE,CAACJ;QACNE,GAAGG,KAAK,CAACL;QACTE,GAAGI,EAAE,CAACX,gBAAgB7B,kBAAkBkC,SAAS1B;KAClD;IAEDkB,UAAUe,OAAO,CAAC,CAACC,UAAUC;QAC3B,MAAMC,UAAU,CAAC,MAAM,EAAED,MAAM,IAAI,CAAC;QACpCN,SAASQ,IAAI,CACX,GAAGtB,gBAAgB,SAAS,EAAEvB,kBAAkBkC,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;QACjCuB,OAAO;QACP,0EAA0E;QAC1E,gEAAgE;QAChEC,QAAQ;YAAC;YAAW;SAAc;QAClCC,SAAS;YAAC,CAAC,gBAAgB,EAAEf,SAAS;SAAC;QACvCgB,UAAU;QACV9B,SAAS;YACPiB;YACAc,UAAU;QACZ;QACAC,WAAW;YAAC3B;SAAiB;IAC/B;IAEA,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAID,oBAAoB,SAAS;QAC/BpB,kCAAkCkB,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.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"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/ import { generateFiles, joinPathFragments, OverwriteStrategy, updateJson } from "@nx/devkit";
|
|
5
5
|
import { addStarExport } from "../ast.js";
|
|
6
6
|
import { esmVars } from "../module-format.js";
|
|
7
|
-
import {
|
|
7
|
+
import { addArtifactProjectToTargets } from "../nx.js";
|
|
8
8
|
import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, SHARED_TERRAFORM_DIR } from "../shared-constructs-constants.js";
|
|
9
9
|
import { terraformProviderVersions } from "../versions.js";
|
|
10
10
|
export const addDynamoDBInfra = async (tree, options)=>{
|
|
@@ -16,7 +16,7 @@ export const addDynamoDBInfra = async (tree, options)=>{
|
|
|
16
16
|
throw new Error(`Unsupported iac ${options.iac}`);
|
|
17
17
|
}
|
|
18
18
|
updateJson(tree, joinPathFragments(PACKAGES_DIR, options.iac === 'cdk' ? SHARED_CONSTRUCTS_DIR : SHARED_TERRAFORM_DIR, 'project.json'), (config)=>{
|
|
19
|
-
|
|
19
|
+
addArtifactProjectToTargets(config, options.projectName);
|
|
20
20
|
return config;
|
|
21
21
|
});
|
|
22
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/dynamodb-constructs/dynamodb-constructs.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { addStarExport } from '../ast.js';\nimport type { Iac } from '../iac.js';\nimport { esmVars } from '../module-format.js';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/dynamodb-constructs/dynamodb-constructs.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { addStarExport } from '../ast.js';\nimport type { Iac } from '../iac.js';\nimport { esmVars } from '../module-format.js';\nimport { addArtifactProjectToTargets } from '../nx.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../shared-constructs-constants.js';\nimport { terraformProviderVersions } from '../versions.js';\n\nexport interface AddDynamoDBConstructOptions {\n projectName: string;\n nameClassName: string;\n nameKebabCase: string;\n tableName: string;\n projectRoot: string;\n}\n\nexport const addDynamoDBInfra = async (\n tree: Tree,\n options: AddDynamoDBConstructOptions & { iac: Iac },\n) => {\n if (options.iac === 'cdk') {\n await addDynamoDBCdkConstructs(tree, options);\n } else if (options.iac === 'terraform') {\n addDynamoDBTerraformModules(tree, options);\n } else {\n throw new Error(`Unsupported iac ${options.iac}`);\n }\n\n updateJson(\n tree,\n joinPathFragments(\n PACKAGES_DIR,\n options.iac === 'cdk' ? SHARED_CONSTRUCTS_DIR : SHARED_TERRAFORM_DIR,\n 'project.json',\n ),\n (config: ProjectConfiguration) => {\n addArtifactProjectToTargets(config, options.projectName);\n return config;\n },\n );\n};\n\nexport const addDynamoDBCdkConstructs = async (\n tree: Tree,\n options: AddDynamoDBConstructOptions,\n) => {\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', 'cdk', 'core'),\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, 'src', 'core'),\n { ...options, ...esmVars(tree) },\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', 'cdk', 'app', 'dynamodb'),\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'app',\n 'dynamodb',\n ),\n { ...options, ...esmVars(tree) },\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n\n await addStarExport(\n tree,\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'core',\n 'index.ts',\n ),\n './dynamodb.js',\n );\n await addStarExport(\n tree,\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'app',\n 'dynamodb',\n 'index.ts',\n ),\n `./${options.nameKebabCase}.js`,\n );\n await addStarExport(\n tree,\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'app',\n 'index.ts',\n ),\n './dynamodb/index.js',\n );\n};\n\nexport const addDynamoDBTerraformModules = (\n tree: Tree,\n options: AddDynamoDBConstructOptions,\n) => {\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', 'terraform', 'core'),\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'src', 'core'),\n { ...options, ...terraformProviderVersions() },\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n 'files',\n 'terraform',\n 'app',\n 'dynamodb',\n ),\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n 'app',\n 'dynamodb',\n ),\n { ...options, ...terraformProviderVersions() },\n {\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n};\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateJson","addStarExport","esmVars","addArtifactProjectToTargets","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","terraformProviderVersions","addDynamoDBInfra","tree","options","iac","addDynamoDBCdkConstructs","addDynamoDBTerraformModules","Error","config","projectName","dirname","overwriteStrategy","KeepExisting","nameKebabCase"],"mappings":"AAAA;;;CAGC,GACD,SACEA,aAAa,EACbC,iBAAiB,EACjBC,iBAAiB,EAGjBC,UAAU,QACL,aAAa;AACpB,SAASC,aAAa,QAAQ,YAAY;AAE1C,SAASC,OAAO,QAAQ,sBAAsB;AAC9C,SAASC,2BAA2B,QAAQ,WAAW;AACvD,SACEC,YAAY,EACZC,qBAAqB,EACrBC,oBAAoB,QACf,oCAAoC;AAC3C,SAASC,yBAAyB,QAAQ,iBAAiB;AAU3D,OAAO,MAAMC,mBAAmB,OAC9BC,MACAC;IAEA,IAAIA,QAAQC,GAAG,KAAK,OAAO;QACzB,MAAMC,yBAAyBH,MAAMC;IACvC,OAAO,IAAIA,QAAQC,GAAG,KAAK,aAAa;QACtCE,4BAA4BJ,MAAMC;IACpC,OAAO;QACL,MAAM,IAAII,MAAM,CAAC,gBAAgB,EAAEJ,QAAQC,GAAG,EAAE;IAClD;IAEAX,WACES,MACAX,kBACEM,cACAM,QAAQC,GAAG,KAAK,QAAQN,wBAAwBC,sBAChD,iBAEF,CAACS;QACCZ,4BAA4BY,QAAQL,QAAQM,WAAW;QACvD,OAAOD;IACT;AAEJ,EAAE;AAEF,OAAO,MAAMH,2BAA2B,OACtCH,MACAC;IAEAb,cACEY,MACAX,kBAAkB,YAAYmB,OAAO,EAAE,SAAS,OAAO,SACvDnB,kBAAkBM,cAAcC,uBAAuB,OAAO,SAC9D;QAAE,GAAGK,OAAO;QAAE,GAAGR,QAAQO,KAAK;IAAC,GAC/B;QACES,mBAAmBnB,kBAAkBoB,YAAY;IACnD;IAGFtB,cACEY,MACAX,kBAAkB,YAAYmB,OAAO,EAAE,SAAS,OAAO,OAAO,aAC9DnB,kBACEM,cACAC,uBACA,OACA,OACA,aAEF;QAAE,GAAGK,OAAO;QAAE,GAAGR,QAAQO,KAAK;IAAC,GAC/B;QACES,mBAAmBnB,kBAAkBoB,YAAY;IACnD;IAGF,MAAMlB,cACJQ,MACAX,kBACEM,cACAC,uBACA,OACA,QACA,aAEF;IAEF,MAAMJ,cACJQ,MACAX,kBACEM,cACAC,uBACA,OACA,OACA,YACA,aAEF,CAAC,EAAE,EAAEK,QAAQU,aAAa,CAAC,GAAG,CAAC;IAEjC,MAAMnB,cACJQ,MACAX,kBACEM,cACAC,uBACA,OACA,OACA,aAEF;AAEJ,EAAE;AAEF,OAAO,MAAMQ,8BAA8B,CACzCJ,MACAC;IAEAb,cACEY,MACAX,kBAAkB,YAAYmB,OAAO,EAAE,SAAS,aAAa,SAC7DnB,kBAAkBM,cAAcE,sBAAsB,OAAO,SAC7D;QAAE,GAAGI,OAAO;QAAE,GAAGH,2BAA2B;IAAC,GAC7C;QACEW,mBAAmBnB,kBAAkBoB,YAAY;IACnD;IAGFtB,cACEY,MACAX,kBACE,YAAYmB,OAAO,EACnB,SACA,aACA,OACA,aAEFnB,kBACEM,cACAE,sBACA,OACA,OACA,aAEF;QAAE,GAAGI,OAAO;QAAE,GAAGH,2BAA2B;IAAC,GAC7C;QACEW,mBAAmBnB,kBAAkBoB,YAAY;IACnD;AAEJ,EAAE"}
|
|
@@ -18,27 +18,12 @@ const isRunning = (): boolean => {
|
|
|
18
18
|
return result.status === 0 && result.stdout.trim() === 'true';
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
// Ensure the image is available locally before starting the container.
|
|
22
|
-
const ensureImage = (): void => {
|
|
23
|
-
const inspect = spawnSync(containerEngine, ['image', 'inspect', image], {
|
|
24
|
-
stdio: 'ignore',
|
|
25
|
-
});
|
|
26
|
-
if (inspect.status !== 0) {
|
|
27
|
-
const pull = spawnSync(containerEngine, ['pull', image], {
|
|
28
|
-
stdio: 'inherit',
|
|
29
|
-
});
|
|
30
|
-
if (pull.status !== 0) process.exit(pull.status ?? 1);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
21
|
// Multiple dynamo table packages run dev in parallel sharing the same
|
|
35
22
|
// container. All processes stream logs, but only the one that created the
|
|
36
23
|
// container stops it on exit — others just detach.
|
|
37
24
|
let created = false;
|
|
38
25
|
let runRetries = 0;
|
|
39
26
|
|
|
40
|
-
ensureImage();
|
|
41
|
-
|
|
42
27
|
for (;;) {
|
|
43
28
|
if (containerExists()) {
|
|
44
29
|
if (!isRunning()) {
|
|
@@ -26,21 +26,6 @@ const isRunning = (): boolean => {
|
|
|
26
26
|
return result.status === 0 && result.stdout.trim() === "true";
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
// Ensure the image is available locally before starting the container.
|
|
30
|
-
const ensureImage = (): void => {
|
|
31
|
-
const inspect = spawnSync(containerEngine, ["image", "inspect", image], {
|
|
32
|
-
stdio: "ignore",
|
|
33
|
-
});
|
|
34
|
-
if (inspect.status !== 0) {
|
|
35
|
-
const pull = spawnSync(containerEngine, ["pull", image], {
|
|
36
|
-
stdio: "inherit",
|
|
37
|
-
});
|
|
38
|
-
if (pull.status !== 0) process.exit(pull.status ?? 1);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
ensureImage();
|
|
43
|
-
|
|
44
29
|
if (containerExists()) {
|
|
45
30
|
if (!isRunning()) {
|
|
46
31
|
const start = spawnSync(containerEngine, ["start", containerName], {
|
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
|
+
* Excludes test reports — including the vendored scripts vitest's coverage HTML
|
|
8
|
+
* reporter writes — from formatting and linting.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BIOME_TEST_OUTPUT_EXCLUDE = "!**/test-output";
|
|
6
11
|
/**
|
|
7
12
|
* The biome.json vended into a new workspace. The pnpm catalog resolver is only
|
|
8
13
|
* included on pnpm workspaces, since `experimentalPnpmCatalogs` is Biome's only
|
package/src/utils/format.js
CHANGED
|
@@ -7,6 +7,10 @@ import path from "path";
|
|
|
7
7
|
import { ruffFixAndFormat } from "./ruff.js";
|
|
8
8
|
import { tryReadToml } from "./toml.js";
|
|
9
9
|
import { TS_VERSIONS } from "./versions.js";
|
|
10
|
+
/**
|
|
11
|
+
* Excludes test reports — including the vendored scripts vitest's coverage HTML
|
|
12
|
+
* reporter writes — from formatting and linting.
|
|
13
|
+
*/ export const BIOME_TEST_OUTPUT_EXCLUDE = '!**/test-output';
|
|
10
14
|
/**
|
|
11
15
|
* The biome.json vended into a new workspace. The pnpm catalog resolver is only
|
|
12
16
|
* included on pnpm workspaces, since `experimentalPnpmCatalogs` is Biome's only
|
|
@@ -64,6 +68,7 @@ import { TS_VERSIONS } from "./versions.js";
|
|
|
64
68
|
'**',
|
|
65
69
|
'!**/dist',
|
|
66
70
|
'!**/out-tsc',
|
|
71
|
+
BIOME_TEST_OUTPUT_EXCLUDE,
|
|
67
72
|
'!**/node_modules',
|
|
68
73
|
'!**/.nx',
|
|
69
74
|
'!**/.venv',
|
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 * 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 '!**/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","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":"AAAA;;;CAGC,GAED,SAASA,KAAK,QAAQ,yBAAyB;AAC/C,SAASC,WAAW,QAAmB,aAAa;AACpD,OAAOC,UAAU,OAAO;AACxB,SAA2BC,gBAAgB,QAAQ,YAAY;AAC/D,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,WAAW,QAAQ,gBAAgB;AAE5C;;;;;CAKC,GACD,OAAO,MAAMC,wBAAwB,CAACC,OAAgB,CAAA;QACpDC,SAAS,CAAC,4BAA4B,EAAEH,WAAW,CAAC,iBAAiB,CAAC,YAAY,CAAC;QACnFI,MAAM;QACNC,WAAW;YACTC,SAAS;YACTC,aAAa;YACbC,aAAa;YACbC,WAAW;QACb;QACAC,YAAY;YACVL,WAAW;gBACTM,YAAY;gBACZC,gBAAgB;YAClB;YACA,+EAA+E;YAC/E,GAAIV,KAAKW,MAAM,CAAC,yBACZ;gBAAEC,UAAU;oBAAEC,0BAA0B;gBAAK;YAAE,IAC/C,CAAC,CAAC;QACR;QACAC,KAAK;YACHX,WAAW;gBACTM,YAAY;YACd;YACAM,QAAQ;gBACNX,SAAS;YACX;QACF;QACAW,QAAQ;YACNX,SAAS;YACTY,OAAO;gBACLC,QAAQ;gBACRC,aAAa;oBACX,qEAAqE;oBACrE,wCAAwC;oBACxCC,0BAA0B;gBAC5B;YACF;QACF;QACAC,QAAQ;YACNC,SAAS;gBACPC,QAAQ;oBACNC,iBAAiB;gBACnB;YACF;QACF;QACAC,OAAO;YACLC,UAAU;gBACR;gBACA;gBACA;gBACA;gBACA;gBACA;gBACA,kEAAkE;gBAClE,8DAA8D;gBAC9D;gBACA;gBACA;gBACA;gBACA;aACD;QACH;QACA,qEAAqE;QACrE,+EAA+E;QAC/EC,WAAW;YACT;gBACED,UAAU;oBACR;oBACA;oBACA;iBACD;gBACDV,QAAQ;oBACNC,OAAO;wBACLE,aAAa;4BACXC,0BAA0B;wBAC5B;oBACF;gBACF;YACF;SACD;IACH,CAAA,EAAG;AAEH,MAAMQ,+BAA+B,IAAIC,IAAI;IAC3C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,mEAAmE,GACnE,MAAMC,aAAa,CAACC,WAClB,6BAA6BC,IAAI,CAACD;AAapC;;;;;;;;CAQC,GACD,MAAME,mBAAmB,IAAIC;AAE7B;;;CAGC,GACD,MAAMC,oBAAoB,CAACJ,WACzBA,SAASK,KAAK,CAACxC,KAAKyC,GAAG,EAAEC,IAAI,CAAC,KAAKC,OAAO,CAAC,SAAS;AAEtD;;;;CAIC,GACD,OAAO,eAAeC,qBACpBvC,IAAU,EACVwC,GAAY,EACZC,OAAqC;IAErC,IAAIC,UAAUV,iBAAiBW,GAAG,CAAC3C;IACnC,IAAIyC,SAASG,QAAQC,QAAQ;QAC3B,IAAI,CAACH,SAAS;YACZA,UAAU,IAAId;YACdI,iBAAiBc,GAAG,CAAC9C,MAAM0C;QAC7B;QACA,KAAK,MAAMZ,YAAYW,QAAQG,MAAM,CAAE;YACrCF,QAAQK,GAAG,CAACb,kBAAkBJ;QAChC;IACF;IACA,MAAMkB,eAAehD,KAClBiD,WAAW,GACXC,MAAM,CAAC,CAACC,OAASA,KAAKC,IAAI,KAAK,UAC/BF,MAAM,CAAC,CAACC,OAAUX,MAAMW,KAAKxD,IAAI,CAAC0D,UAAU,CAACb,OAAO,MACpDU,MAAM,CAAC,CAACC,OAAS,CAACT,SAASY,IAAIpB,kBAAkBiB,KAAKxD,IAAI;IAE7D,MAAM4D,UAAUP,aAAaE,MAAM,CAAC,CAACC,OAASA,KAAKxD,IAAI,CAAC6D,QAAQ,CAAC;IACjE,MAAMC,aAAaT,aAAaE,MAAM,CACpC,CAACC,OACCxB,6BAA6B2B,GAAG,CAAC3D,KAAK+D,OAAO,CAACP,KAAKxD,IAAI,MACvD,oEAAoE;QACpE,yEAAyE;QACzE,oEAAoE;QACpE,yEAAyE;QACzE,wCAAwC;QACxC,CAACkC,WAAWsB,KAAKxD,IAAI;IAGzB,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAMgE,uBAAuBJ,QAAQV,MAAM,GACvCe,4BAA4B5D,QAC5B,EAAE;IAEN,0DAA0D;IAC1D,KAAK,MAAMmD,QAAQI,QAAS;QAC1B,IAAI;YACF,MAAMM,UAAUjE,iBACduD,KAAKU,OAAO,CAACC,QAAQ,CAAC,UACtBX,KAAKxD,IAAI,EACToE,mBACEC,eAAehE,MAAMmD,KAAKxD,IAAI,GAC9BsE,2BAA2Bd,KAAKxD,IAAI,EAAEgE;YAG1C3D,KAAKkE,KAAK,CAACf,KAAKxD,IAAI,EAAEkE;QACxB,EAAE,OAAM;QACN,yCAAyC;QAC3C;IACF;IAEA,IAAIJ,WAAWZ,MAAM,KAAK,GAAG;IAE7BsB,gBAAgBnE,MAAMyD;AACxB;AAEA;;;;;;;;CAQC,GACD,SAASW,gBAAgBpE,IAAU;IACjC,MAAMqE,SAASrE,KAAKsE,IAAI,CAAC,cAAc;IACvC,IAAID,QAAQ;QACV,IAAI;YACF,OAAOE,KAAKC,KAAK,CAACH;QACpB,EAAE,OAAM;QACN,wDAAwD;QAC1D;IACF;IACA,OAAOtE,sBAAsBC;AAC/B;AAEA;;;;;;;;;;CAUC,GACD,SAASmE,gBACPnE,IAAU,EACVwB,KAAiD;IAEjD,IAAI;QACF,MAAMiD,QAAQ,IAAIhF;QAClB,MAAM,EAAEiF,UAAU,EAAE,GAAGD,MAAME,WAAW;QACxCF,MAAMG,kBAAkB,CAACF,YAAYN,gBAAgBpE;QAErD,KAAK,MAAMmD,QAAQ3B,MAAO;YACxB,IAAI;gBACF,MAAM,EAAEqC,OAAO,EAAE,GAAGY,MAAMI,aAAa,CACrCH,YACAvB,KAAKU,OAAO,EAAEC,SAAS,YAAY,IACnC;oBAAEhC,UAAUqB,KAAKxD,IAAI;gBAAC;gBAExBK,KAAKkE,KAAK,CAACf,KAAKxD,IAAI,EAAEkE;YACxB,EAAE,OAAM;YACN,uDAAuD;YACzD;QACF;IACF,EAAE,OAAM;IACN,oCAAoC;IACtC;AACF;AAEA;;;;;;;;;;;;;;;CAeC,GACD,SAASG,eAAehE,IAAU,EAAE8B,QAAgB;IAClD,IAAIU,MAAM7C,KAAKmF,OAAO,CAAChD;IACvB,MAAO,KAAM;QACX,KAAK,MAAMiD,QAAQ;YAAC;YAAc;SAAY,CAAE;YAC9C,MAAMV,SAASxE,YAAYG,MAAML,KAAK0C,IAAI,CAACG,KAAKuC;YAChD,IAAIV,QAAQ;gBACV,OAAOA;YACT;QACF;QACA,MAAMW,OAAQnF,YAAYG,MAAML,KAAK0C,IAAI,CAACG,KAAK,oBAC3CyC,MAAMD;QACV,IAAIA,MAAM;YACR,OAAOA;QACT;QACA,uDAAuD;QACvD,IAAIxC,QAAQ,OAAOA,QAAQ,MAAMA,QAAQ7C,KAAKmF,OAAO,CAACtC,MAAM;YAC1D,OAAO0C;QACT;QACA1C,MAAM7C,KAAKmF,OAAO,CAACtC;IACrB;AACF;AAEA;;;;;CAKC,GACD,MAAM2C,sBAAsB;IAAC;IAAK;IAAK;IAAM;IAAK;IAAO;CAAI;AAmB7D;;;;CAIC,GACD,OAAO,MAAMC,6BAA6B,CACxCC;IAEA,IAAI,OAAOA,mBAAmB,UAAU;QACtC,OAAOH;IACT;IACA,IAAII;IACJ,KAAK,MAAMC,SAASF,eAAeG,QAAQ,CAAC,iBAAkB;QAC5D,MAAMC,QAAQC,OAAOH,KAAK,CAAC,EAAE;QAC7B,MAAMI,QAAQD,OAAOH,KAAK,CAAC,EAAE;QAC7B,IACE,CAACD,OACDG,QAAQH,IAAIG,KAAK,IAChBA,UAAUH,IAAIG,KAAK,IAAIE,QAAQL,IAAIK,KAAK,EACzC;YACAL,MAAM;gBAAEG;gBAAOE;YAAM;QACvB;IACF;IACA,OAAOL,MAAM,CAAC,EAAE,EAAEA,IAAIG,KAAK,GAAGH,IAAIK,KAAK,EAAE,GAAGT;AAC9C,EAAE;AAEF;;;;;CAKC,GACD,SAAStB,4BAA4B5D,IAAU;IAC7C,MAAM4F,UAAqC,EAAE;IAE7C,KAAK,MAAMC,WAAWnG,YAAYM,MAAM8F,MAAM,GAAI;QAChD,4EAA4E;QAC5E,+BAA+B;QAC/B,MAAMC,YAAYlG,YAChBG,MACAL,KAAK0C,IAAI,CAACwD,QAAQ3F,IAAI,EAAE;QAE1B,IAAI,CAAC6F,WAAW;YACd;QACF;QACA,MAAMC,gBACJD,WAAWd,MAAMgB,OAAOC,OAAOC,SAASC,OAAOC;QACjD,qEAAqE;QACrE,oCAAoC;QACpC,MAAMC,UAAUC,MAAMC,OAAO,CAACR,iBAC1BA,cACG9C,MAAM,CAAC,CAACuD,MAAuB,OAAOA,QAAQ,YAAY,CAAC,CAACA,KAC5DC,GAAG,CAAC,CAACD,MAAQA,IAAItE,KAAK,CAAC,IAAI,CAAC,EAAE,IACjC,EAAE;QACN,MAAMwE,aAAsBZ,WAAWd,MAAMD,MAAM,CAAC,cAAc;QAClE,MAAM4B,gBAAgBxB,2BACpBW,WAAWF,SAAS,CAAC,kBAAkB;QAEzC,MAAMgB,WAAoBd,WAAWd,MAAMD,MAAM8B,MAAMC;QACvD,MAAMA,SAASR,MAAMC,OAAO,CAACK,YACzBA,SAAS3D,MAAM,CACb,CAAC8D,OAAyB,OAAOA,SAAS,YAAY,CAAC,CAACA,QAE1D9B;QACJ,IACEoB,QAAQzD,MAAM,IACd,OAAO8D,eAAe,YACtBC,iBACAG,QAAQlE,QACR;YACA+C,QAAQqB,IAAI,CAAC;gBACX/G,MAAM2F,QAAQ3F,IAAI,CAACiC,KAAK,CAACxC,KAAKyC,GAAG,EAAEC,IAAI,CAAC;gBACxCiE;gBACAK,YAAY,OAAOA,eAAe,WAAWA,aAAazB;gBAC1D0B;gBACAG,QAAQA,QAAQlE,SAASkE,SAAS7B;YACpC;QACF;IACF;IAEA,OAAOU;AACT;AAEA;;;;;;;CAOC,GACD,SAAS3B,2BACPnC,QAAgB,EAChB8D,OAAkC;IAElC,IAAIsB;IACJ,KAAK,MAAM7C,UAAUuB,QAAS;QAC5B,IACE,AAAC9D,CAAAA,aAAauC,OAAOnE,IAAI,IAAI4B,SAASuB,UAAU,CAAC,GAAGgB,OAAOnE,IAAI,CAAC,CAAC,CAAC,CAAA,KACjE,CAAA,CAACgH,SAAS7C,OAAOnE,IAAI,CAAC2C,MAAM,GAAGqE,MAAMhH,IAAI,CAAC2C,MAAM,AAAD,GAChD;YACAqE,QAAQ7C;QACV;IACF;IACA,OAAO6C;AACT;AAEA;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,SAASnD,mBACPM,MAA+B,EAC/B8C,aAAuC;IAEvC,MAAML,OAAgC;QAAE,GAAGzC,QAAQyC,IAAI;IAAC;IACxD,sEAAsE;IACtE,8DAA8D;IAC9D,IAAI,CAACzC,QAAQ;QACXyC,KAAKC,MAAM,GAAGI,eAAeJ,UAAU5B;IACzC;IACA,IAAIgC,eAAeb,QAAQzD,QAAQ;QACjCiE,KAAKM,KAAK,GAAG;YACX,GAAIN,KAAKM,KAAK;YACd,qBAAqBD,cAAcb,OAAO;QAC5C;IACF;IACA,OAAO;QACL,GAAGjC,MAAM;QACT,GAAI,OAAO8C,eAAeR,eAAe,WACrC;YAAE,eAAeQ,cAAcR,UAAU;QAAC,IAC1C,CAAC,CAAC;QACN,GAAIQ,eAAeP,gBACf;YAAE,kBAAkBO,cAAcP,aAAa;QAAC,IAChD,CAAC,CAAC;QACNE;IACF;AACF"}
|
|
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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"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/ import { generateFiles, joinPathFragments, OverwriteStrategy, updateJson } from "@nx/devkit";
|
|
5
5
|
import { addStarExport } from "../ast.js";
|
|
6
6
|
import { esmVars } from "../module-format.js";
|
|
7
|
-
import {
|
|
7
|
+
import { addArtifactProjectToTargets } from "../nx.js";
|
|
8
8
|
import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, SHARED_TERRAFORM_DIR } from "../shared-constructs-constants.js";
|
|
9
9
|
import { cdkLambdaRuntime, terraformLambdaRuntime, terraformProviderVersions } from "../versions.js";
|
|
10
10
|
/**
|
|
@@ -18,7 +18,7 @@ import { cdkLambdaRuntime, terraformLambdaRuntime, terraformProviderVersions } f
|
|
|
18
18
|
throw new Error(`Unsupported iac ${options.iac}`);
|
|
19
19
|
}
|
|
20
20
|
updateJson(tree, joinPathFragments(PACKAGES_DIR, options.iac === 'cdk' ? SHARED_CONSTRUCTS_DIR : SHARED_TERRAFORM_DIR, 'project.json'), (config)=>{
|
|
21
|
-
|
|
21
|
+
addArtifactProjectToTargets(config, options.functionProjectName);
|
|
22
22
|
return config;
|
|
23
23
|
});
|
|
24
24
|
};
|