@aws/nx-plugin 1.0.0-rc.47 → 1.0.0-rc.48
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/package.json +1 -4
- package/src/mcp-server/dev-server.d.ts +1 -0
- package/src/mcp-server/dev-server.js +18 -0
- package/src/mcp-server/dev-server.js.map +1 -0
- package/src/preset/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +7 -3
- package/src/ts/agent/files/deploy/Dockerfile.template +7 -3
- package/src/ts/agent/generator.js +2 -2
- package/src/ts/agent/generator.js.map +1 -1
- package/src/ts/mcp-server/files/Dockerfile.template +7 -3
- package/src/ts/mcp-server/generator.js +2 -2
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/nx-plugin/utils.js +7 -10
- package/src/ts/nx-plugin/utils.js.map +1 -1
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +11 -3
- package/src/ts/rdb/files/Dockerfile.template +5 -1
- package/src/ts/rdb/files/src/migration-handler.ts.template +3 -1
- package/src/ts/rdb/generator.js +2 -2
- package/src/ts/rdb/generator.js.map +1 -1
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +19 -1
- package/src/utils/docker.d.ts +8 -0
- package/src/utils/docker.js +8 -1
- package/src/utils/docker.js.map +1 -1
- package/src/utils/format.js +54 -25
- package/src/utils/format.js.map +1 -1
- package/src/utils/init.js +7 -2
- package/src/utils/init.js.map +1 -1
- package/src/utils/mcp.d.ts +9 -0
- package/src/utils/mcp.js +9 -1
- package/src/utils/mcp.js.map +1 -1
- package/src/utils/nx.d.ts +13 -0
- package/src/utils/nx.js +24 -1
- package/src/utils/nx.js.map +1 -1
- package/src/utils/test-git.d.ts +39 -0
- package/src/utils/test-git.js +117 -0
- package/src/utils/test-git.js.map +1 -0
- package/src/utils/versions.d.ts +4 -1
- package/src/utils/versions.js +10 -2
- package/src/utils/versions.js.map +1 -1
- package/src/bin/aws-nx-mcp.d.ts +0 -2
- package/src/bin/aws-nx-mcp.js +0 -14
- package/src/bin/aws-nx-mcp.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/ts/rdb/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { relative } from 'node:path';\nimport {\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n readProjectConfiguration,\n type Tree,\n updateJson,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { addTypeScriptBundleTarget } from '../../utils/bundle/bundle';\nimport { resolveContainers } from '../../utils/containers';\nimport { addDependenciesToPackageJson } from '../../utils/dependencies';\nimport { addDockerScanTarget } from '../../utils/docker';\nimport { formatFilesInSubtree } from '../../utils/format';\nimport { FsCommands } from '../../utils/fs';\nimport { updateGitIgnore } from '../../utils/git';\nimport { resolveIac } from '../../utils/iac';\nimport { installDependencies } from '../../utils/install';\nimport { addGeneratorMetricsIfApplicable } from '../../utils/metrics';\nimport { esmVars } from '../../utils/module-format';\nimport { kebabCase, snakeCase, toClassName } from '../../utils/names';\nimport { getNpmScope } from '../../utils/npm-scope';\nimport {\n addDependencyToTargetIfNotPresent,\n addGeneratorMetadata,\n getGeneratorInfo,\n type NxGeneratorInfo,\n} from '../../utils/nx';\nimport { getRelativePathToRootByDirectory } from '../../utils/paths';\nimport { registerPnpmBuiltDependencies } from '../../utils/pnpm-workspace';\nimport { assignPort } from '../../utils/port';\nimport { addRdbInfra } from '../../utils/rdb-constructs/rdb-constructs';\nimport { sharedConstructsGenerator } from '../../utils/shared-constructs';\nimport {\n PACKAGES_DIR,\n SHARED_SCRIPTS_DIR,\n} from '../../utils/shared-constructs-constants';\nimport { sharedRdbScriptsGenerator } from '../../utils/shared-rdb-scripts';\nimport { TS_VERSIONS, withVersions } from '../../utils/versions';\nimport tsProjectGenerator, { getTsLibDetails } from '../lib/generator';\nimport type { TsRdbGeneratorSchema } from './schema';\n\nexport const TS_RDB_GENERATOR_INFO: NxGeneratorInfo = getGeneratorInfo(\n import.meta.filename,\n);\n\nexport const tsRdbGenerator = async (\n tree: Tree,\n options: TsRdbGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const nameKebabCase = kebabCase(options.name);\n const nameClassName = toClassName(options.name);\n const databaseUser = options.databaseUser ?? 'dbadmin';\n const databaseName = snakeCase(options.databaseName ?? options.name);\n const containerEngine = await resolveContainers(tree, 'inherit');\n const { fullyQualifiedName, dir } = getTsLibDetails(tree, {\n name: options.name,\n directory: options.directory,\n subDirectory: options.subDirectory,\n });\n\n let projectExists: boolean;\n try {\n readProjectConfiguration(tree, fullyQualifiedName);\n projectExists = true;\n } catch {\n projectExists = false;\n }\n\n if (!projectExists) {\n await tsProjectGenerator(tree, {\n name: options.name,\n directory: options.directory,\n preferInstallDependencies: false,\n });\n }\n\n updateJson(tree, joinPathFragments(dir, 'tsconfig.lib.json'), (tsConfig) => ({\n ...tsConfig,\n include: ['src/**/*.ts', 'generated/prisma/**/*.ts'],\n }));\n\n const projectConfig = readProjectConfiguration(tree, fullyQualifiedName);\n const localDbPort = assignPort(\n tree,\n projectConfig,\n options.engine === 'mysql' ? 3306 : 5432,\n );\n const localDbHost = 'localhost';\n const localDbUser = options.engine === 'mysql' ? 'root' : 'dbadmin';\n const localDbPassword = 'password';\n const containerName = `${getNpmScope(tree)}-${databaseName}`;\n const dockerImage =\n options.engine === 'mysql'\n ? 'public.ecr.aws/docker/library/mysql:8.0.44'\n : 'public.ecr.aws/docker/library/postgres:17.7';\n\n const templateOptions = {\n engine: options.engine,\n runtimeConfigKey: nameClassName,\n databasePackageAlias: fullyQualifiedName,\n databaseProvider: options.engine === 'mysql' ? 'mysql' : 'postgresql',\n prismaVersion: TS_VERSIONS.prisma,\n npmVersion: TS_VERSIONS.npm,\n prismaAdapterPackage:\n options.engine === 'mysql'\n ? '@prisma/adapter-mariadb'\n : '@prisma/adapter-pg',\n prismaAdapterClassName:\n options.engine === 'mysql' ? 'PrismaMariaDb' : 'PrismaPg',\n localDbPort,\n localDbHost,\n localDbName: databaseName,\n localDbUser,\n localDbPassword,\n containerEngine,\n containerName,\n dockerImage,\n ...esmVars(tree),\n };\n\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files'),\n dir,\n templateOptions,\n );\n updateGitIgnore(tree, dir, (patterns) => [...patterns, 'generated/prisma']);\n await sharedRdbScriptsGenerator(tree, options.engine);\n const waitForDbScript =\n options.engine === 'mysql'\n ? 'wait-for-mysql-db.ts'\n : 'wait-for-postgres-db.ts';\n const scriptsDir = relative(\n dir,\n joinPathFragments(PACKAGES_DIR, SHARED_SCRIPTS_DIR, 'src', 'rdb'),\n );\n const relativePathToRoot = getRelativePathToRootByDirectory(\n projectConfig.root,\n );\n const fs = new FsCommands(tree);\n const migrationBundleDir = joinPathFragments(\n 'dist',\n projectConfig.root,\n 'bundle',\n 'migration',\n );\n const migrationDockerImageTag = `${getNpmScope(tree)}-${kebabCase(options.name)}-migration:latest`;\n\n if (options.infra !== 'none') {\n await addTypeScriptBundleTarget(tree, projectConfig, {\n targetFilePath: 'src/migration-handler.ts',\n bundleOutputDir: 'migration',\n });\n await addTypeScriptBundleTarget(tree, projectConfig, {\n targetFilePath: 'src/create-db-user-handler.ts',\n bundleOutputDir: 'create-db-user',\n });\n const bundleTarget = projectConfig.targets['bundle'];\n // The bundle target starts with a single rolldown `command`. Wrap it with\n // the migration asset copy steps, unless it has already been transformed\n // into a `commands` array on a previous run.\n if (!bundleTarget.options.commands) {\n const rolldownCommand = bundleTarget.options.command;\n delete bundleTarget.options.command;\n bundleTarget.options = {\n ...bundleTarget.options,\n commands: [\n fs.rm(`${relativePathToRoot}dist/{projectRoot}/bundle/migration`),\n fs.mkdir(`${relativePathToRoot}dist/{projectRoot}/bundle/migration`),\n fs.cp(\n 'prisma',\n `${relativePathToRoot}dist/{projectRoot}/bundle/migration/prisma`,\n ),\n fs.cp(\n 'prisma.config.ts',\n `${relativePathToRoot}dist/{projectRoot}/bundle/migration/prisma.config.ts`,\n ),\n fs.cp(\n 'Dockerfile',\n `${relativePathToRoot}dist/{projectRoot}/bundle/migration/Dockerfile`,\n ),\n rolldownCommand,\n ],\n parallel: false,\n };\n }\n }\n\n projectConfig.targets.generate = {\n executor: 'nx:run-commands',\n outputs: ['{projectRoot}/generated/prisma'],\n options: {\n command: 'prisma generate',\n cwd: '{projectRoot}',\n },\n };\n projectConfig.targets['pull-image'] = {\n executor: 'nx:run-commands',\n options: {\n command: `tsx ${scriptsDir}/pull-image.ts`,\n cwd: '{projectRoot}',\n },\n };\n projectConfig.targets['dev'] = {\n executor: 'nx:run-commands',\n options: {\n command: `tsx ${scriptsDir}/start-container.ts`,\n cwd: '{projectRoot}',\n },\n continuous: true,\n };\n projectConfig.targets['wait-for-db'] = {\n executor: 'nx:run-commands',\n dependsOn: ['dev'],\n options: {\n command: `tsx ${scriptsDir}/${waitForDbScript}`,\n cwd: '{projectRoot}',\n },\n };\n projectConfig.targets.prisma = {\n executor: 'nx:run-commands',\n dependsOn: ['dev', 'wait-for-db'],\n options: {\n cwd: '{projectRoot}',\n command: 'prisma',\n env: {\n LOCAL_DEV: 'true',\n },\n },\n };\n if (options.infra !== 'none') {\n const iac = await resolveIac(tree, options.iac);\n if (iac === 'terraform') {\n projectConfig.targets['docker'] = {\n cache: true,\n executor: 'nx:run-commands',\n options: {\n command: `${containerEngine} build --platform linux/arm64 --provenance=false -t ${migrationDockerImageTag} ${migrationBundleDir}`,\n },\n dependsOn: ['bundle'],\n };\n addDependencyToTargetIfNotPresent(projectConfig, 'build', 'docker');\n\n addDockerScanTarget(tree, {\n project: projectConfig,\n containerEngine,\n trivyTargetName: 'trivy',\n dockerTargetName: 'docker',\n imageTags: [migrationDockerImageTag],\n });\n }\n addDependencyToTargetIfNotPresent(projectConfig, 'build', 'bundle');\n }\n addDependencyToTargetIfNotPresent(projectConfig, 'compile', 'generate');\n updateProjectConfiguration(tree, fullyQualifiedName, projectConfig);\n addGeneratorMetadata(tree, fullyQualifiedName, TS_RDB_GENERATOR_INFO, {\n engine: options.engine,\n });\n\n if (options.infra !== 'none') {\n const iac = await resolveIac(tree, options.iac);\n await sharedConstructsGenerator(tree, { iac });\n await addRdbInfra(tree, {\n iac,\n projectName: fullyQualifiedName,\n projectRoot: dir,\n nameClassName,\n nameKebabCase,\n databasePackageAlias: fullyQualifiedName,\n databaseName,\n adminUser: databaseUser,\n engine: options.engine,\n migrationBundleDir,\n createDbUserBundleDir: joinPathFragments(\n 'dist',\n projectConfig.root,\n 'bundle',\n 'create-db-user',\n ),\n framework: options.framework,\n migrationDockerImageTag,\n containerEngine,\n });\n }\n\n addDependenciesToPackageJson(\n tree,\n withVersions([\n '@aws-lambda-powertools/parameters',\n '@aws-sdk/client-appconfigdata',\n '@aws-sdk/client-secrets-manager',\n '@aws-sdk/rds-signer',\n '@prisma/client',\n ...(options.engine === 'mysql'\n ? (['@prisma/adapter-mariadb', 'mariadb'] as const)\n : (['@prisma/adapter-pg', 'pg'] as const)),\n ]),\n withVersions([\n '@types/aws-lambda',\n ...(options.engine === 'mysql'\n ? ([] as const)\n : (['@types/pg'] as const)),\n ]),\n joinPathFragments(projectConfig.root, 'package.json'),\n );\n // The prisma CLI and tsx run migration/seed scripts from the root.\n addDependenciesToPackageJson(tree, {}, withVersions(['prisma', 'tsx']));\n\n registerPnpmBuiltDependencies(tree, {\n '@prisma/engines': false,\n prisma: false,\n });\n\n await addGeneratorMetricsIfApplicable(tree, [TS_RDB_GENERATOR_INFO]);\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, options.preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\nexport default tsRdbGenerator;\n"],"names":["relative","generateFiles","joinPathFragments","readProjectConfiguration","updateJson","updateProjectConfiguration","addTypeScriptBundleTarget","resolveContainers","addDependenciesToPackageJson","addDockerScanTarget","formatFilesInSubtree","FsCommands","updateGitIgnore","resolveIac","installDependencies","addGeneratorMetricsIfApplicable","esmVars","kebabCase","snakeCase","toClassName","getNpmScope","addDependencyToTargetIfNotPresent","addGeneratorMetadata","getGeneratorInfo","getRelativePathToRootByDirectory","registerPnpmBuiltDependencies","assignPort","addRdbInfra","sharedConstructsGenerator","PACKAGES_DIR","SHARED_SCRIPTS_DIR","sharedRdbScriptsGenerator","TS_VERSIONS","withVersions","tsProjectGenerator","getTsLibDetails","TS_RDB_GENERATOR_INFO","filename","tsRdbGenerator","tree","options","nameKebabCase","name","nameClassName","databaseUser","databaseName","containerEngine","fullyQualifiedName","dir","directory","subDirectory","projectExists","preferInstallDependencies","tsConfig","include","projectConfig","localDbPort","engine","localDbHost","localDbUser","localDbPassword","containerName","dockerImage","templateOptions","runtimeConfigKey","databasePackageAlias","databaseProvider","prismaVersion","prisma","npmVersion","npm","prismaAdapterPackage","prismaAdapterClassName","localDbName","dirname","patterns","waitForDbScript","scriptsDir","relativePathToRoot","root","fs","migrationBundleDir","migrationDockerImageTag","infra","targetFilePath","bundleOutputDir","bundleTarget","targets","commands","rolldownCommand","command","rm","mkdir","cp","parallel","generate","executor","outputs","cwd","continuous","dependsOn","env","LOCAL_DEV","iac","cache","project","trivyTargetName","dockerTargetName","imageTags","projectName","projectRoot","adminUser","createDbUserBundleDir","framework","languages"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/ts/rdb/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { relative } from 'node:path';\nimport {\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n readProjectConfiguration,\n type Tree,\n updateJson,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { addTypeScriptBundleTarget } from '../../utils/bundle/bundle';\nimport { resolveContainers } from '../../utils/containers';\nimport { addDependenciesToPackageJson } from '../../utils/dependencies';\nimport { addDockerScanTarget, nodeImageVersions } from '../../utils/docker';\nimport { formatFilesInSubtree } from '../../utils/format';\nimport { FsCommands } from '../../utils/fs';\nimport { updateGitIgnore } from '../../utils/git';\nimport { resolveIac } from '../../utils/iac';\nimport { installDependencies } from '../../utils/install';\nimport { addGeneratorMetricsIfApplicable } from '../../utils/metrics';\nimport { esmVars } from '../../utils/module-format';\nimport { kebabCase, snakeCase, toClassName } from '../../utils/names';\nimport { getNpmScope } from '../../utils/npm-scope';\nimport {\n addDependencyToTargetIfNotPresent,\n addGeneratorMetadata,\n getGeneratorInfo,\n type NxGeneratorInfo,\n} from '../../utils/nx';\nimport { getRelativePathToRootByDirectory } from '../../utils/paths';\nimport { registerPnpmBuiltDependencies } from '../../utils/pnpm-workspace';\nimport { assignPort } from '../../utils/port';\nimport { addRdbInfra } from '../../utils/rdb-constructs/rdb-constructs';\nimport { sharedConstructsGenerator } from '../../utils/shared-constructs';\nimport {\n PACKAGES_DIR,\n SHARED_SCRIPTS_DIR,\n} from '../../utils/shared-constructs-constants';\nimport { sharedRdbScriptsGenerator } from '../../utils/shared-rdb-scripts';\nimport { TS_VERSIONS, withVersions } from '../../utils/versions';\nimport tsProjectGenerator, { getTsLibDetails } from '../lib/generator';\nimport type { TsRdbGeneratorSchema } from './schema';\n\nexport const TS_RDB_GENERATOR_INFO: NxGeneratorInfo = getGeneratorInfo(\n import.meta.filename,\n);\n\nexport const tsRdbGenerator = async (\n tree: Tree,\n options: TsRdbGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const nameKebabCase = kebabCase(options.name);\n const nameClassName = toClassName(options.name);\n const databaseUser = options.databaseUser ?? 'dbadmin';\n const databaseName = snakeCase(options.databaseName ?? options.name);\n const containerEngine = await resolveContainers(tree, 'inherit');\n const { fullyQualifiedName, dir } = getTsLibDetails(tree, {\n name: options.name,\n directory: options.directory,\n subDirectory: options.subDirectory,\n });\n\n let projectExists: boolean;\n try {\n readProjectConfiguration(tree, fullyQualifiedName);\n projectExists = true;\n } catch {\n projectExists = false;\n }\n\n if (!projectExists) {\n await tsProjectGenerator(tree, {\n name: options.name,\n directory: options.directory,\n preferInstallDependencies: false,\n });\n }\n\n updateJson(tree, joinPathFragments(dir, 'tsconfig.lib.json'), (tsConfig) => ({\n ...tsConfig,\n include: ['src/**/*.ts', 'generated/prisma/**/*.ts'],\n }));\n\n const projectConfig = readProjectConfiguration(tree, fullyQualifiedName);\n const localDbPort = assignPort(\n tree,\n projectConfig,\n options.engine === 'mysql' ? 3306 : 5432,\n );\n const localDbHost = 'localhost';\n const localDbUser = options.engine === 'mysql' ? 'root' : 'dbadmin';\n const localDbPassword = 'password';\n const containerName = `${getNpmScope(tree)}-${databaseName}`;\n const dockerImage =\n options.engine === 'mysql'\n ? 'public.ecr.aws/docker/library/mysql:8.0.44'\n : 'public.ecr.aws/docker/library/postgres:17.7';\n\n const templateOptions = {\n engine: options.engine,\n runtimeConfigKey: nameClassName,\n databasePackageAlias: fullyQualifiedName,\n databaseProvider: options.engine === 'mysql' ? 'mysql' : 'postgresql',\n prismaVersion: TS_VERSIONS.prisma,\n ...nodeImageVersions(),\n prismaAdapterPackage:\n options.engine === 'mysql'\n ? '@prisma/adapter-mariadb'\n : '@prisma/adapter-pg',\n prismaAdapterClassName:\n options.engine === 'mysql' ? 'PrismaMariaDb' : 'PrismaPg',\n localDbPort,\n localDbHost,\n localDbName: databaseName,\n localDbUser,\n localDbPassword,\n containerEngine,\n containerName,\n dockerImage,\n ...esmVars(tree),\n };\n\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files'),\n dir,\n templateOptions,\n );\n updateGitIgnore(tree, dir, (patterns) => [...patterns, 'generated/prisma']);\n await sharedRdbScriptsGenerator(tree, options.engine);\n const waitForDbScript =\n options.engine === 'mysql'\n ? 'wait-for-mysql-db.ts'\n : 'wait-for-postgres-db.ts';\n const scriptsDir = relative(\n dir,\n joinPathFragments(PACKAGES_DIR, SHARED_SCRIPTS_DIR, 'src', 'rdb'),\n );\n const relativePathToRoot = getRelativePathToRootByDirectory(\n projectConfig.root,\n );\n const fs = new FsCommands(tree);\n const migrationBundleDir = joinPathFragments(\n 'dist',\n projectConfig.root,\n 'bundle',\n 'migration',\n );\n const migrationDockerImageTag = `${getNpmScope(tree)}-${kebabCase(options.name)}-migration:latest`;\n\n if (options.infra !== 'none') {\n await addTypeScriptBundleTarget(tree, projectConfig, {\n targetFilePath: 'src/migration-handler.ts',\n bundleOutputDir: 'migration',\n });\n await addTypeScriptBundleTarget(tree, projectConfig, {\n targetFilePath: 'src/create-db-user-handler.ts',\n bundleOutputDir: 'create-db-user',\n });\n const bundleTarget = projectConfig.targets['bundle'];\n // The bundle target starts with a single rolldown `command`. Wrap it with\n // the migration asset copy steps, unless it has already been transformed\n // into a `commands` array on a previous run.\n if (!bundleTarget.options.commands) {\n const rolldownCommand = bundleTarget.options.command;\n delete bundleTarget.options.command;\n bundleTarget.options = {\n ...bundleTarget.options,\n commands: [\n fs.rm(`${relativePathToRoot}dist/{projectRoot}/bundle/migration`),\n fs.mkdir(`${relativePathToRoot}dist/{projectRoot}/bundle/migration`),\n fs.cp(\n 'prisma',\n `${relativePathToRoot}dist/{projectRoot}/bundle/migration/prisma`,\n ),\n fs.cp(\n 'prisma.config.ts',\n `${relativePathToRoot}dist/{projectRoot}/bundle/migration/prisma.config.ts`,\n ),\n fs.cp(\n 'Dockerfile',\n `${relativePathToRoot}dist/{projectRoot}/bundle/migration/Dockerfile`,\n ),\n rolldownCommand,\n ],\n parallel: false,\n };\n }\n }\n\n projectConfig.targets.generate = {\n executor: 'nx:run-commands',\n outputs: ['{projectRoot}/generated/prisma'],\n options: {\n command: 'prisma generate',\n cwd: '{projectRoot}',\n },\n };\n projectConfig.targets['pull-image'] = {\n executor: 'nx:run-commands',\n options: {\n command: `tsx ${scriptsDir}/pull-image.ts`,\n cwd: '{projectRoot}',\n },\n };\n projectConfig.targets['dev'] = {\n executor: 'nx:run-commands',\n options: {\n command: `tsx ${scriptsDir}/start-container.ts`,\n cwd: '{projectRoot}',\n },\n continuous: true,\n };\n projectConfig.targets['wait-for-db'] = {\n executor: 'nx:run-commands',\n dependsOn: ['dev'],\n options: {\n command: `tsx ${scriptsDir}/${waitForDbScript}`,\n cwd: '{projectRoot}',\n },\n };\n projectConfig.targets.prisma = {\n executor: 'nx:run-commands',\n dependsOn: ['dev', 'wait-for-db'],\n options: {\n cwd: '{projectRoot}',\n command: 'prisma',\n env: {\n LOCAL_DEV: 'true',\n },\n },\n };\n if (options.infra !== 'none') {\n const iac = await resolveIac(tree, options.iac);\n if (iac === 'terraform') {\n projectConfig.targets['docker'] = {\n cache: true,\n executor: 'nx:run-commands',\n options: {\n command: `${containerEngine} build --platform linux/arm64 --provenance=false -t ${migrationDockerImageTag} ${migrationBundleDir}`,\n },\n dependsOn: ['bundle'],\n };\n addDependencyToTargetIfNotPresent(projectConfig, 'build', 'docker');\n\n addDockerScanTarget(tree, {\n project: projectConfig,\n containerEngine,\n trivyTargetName: 'trivy',\n dockerTargetName: 'docker',\n imageTags: [migrationDockerImageTag],\n });\n }\n addDependencyToTargetIfNotPresent(projectConfig, 'build', 'bundle');\n }\n addDependencyToTargetIfNotPresent(projectConfig, 'compile', 'generate');\n updateProjectConfiguration(tree, fullyQualifiedName, projectConfig);\n addGeneratorMetadata(tree, fullyQualifiedName, TS_RDB_GENERATOR_INFO, {\n engine: options.engine,\n });\n\n if (options.infra !== 'none') {\n const iac = await resolveIac(tree, options.iac);\n await sharedConstructsGenerator(tree, { iac });\n await addRdbInfra(tree, {\n iac,\n projectName: fullyQualifiedName,\n projectRoot: dir,\n nameClassName,\n nameKebabCase,\n databasePackageAlias: fullyQualifiedName,\n databaseName,\n adminUser: databaseUser,\n engine: options.engine,\n migrationBundleDir,\n createDbUserBundleDir: joinPathFragments(\n 'dist',\n projectConfig.root,\n 'bundle',\n 'create-db-user',\n ),\n framework: options.framework,\n migrationDockerImageTag,\n containerEngine,\n });\n }\n\n addDependenciesToPackageJson(\n tree,\n withVersions([\n '@aws-lambda-powertools/parameters',\n '@aws-sdk/client-appconfigdata',\n '@aws-sdk/client-secrets-manager',\n '@aws-sdk/rds-signer',\n '@prisma/client',\n ...(options.engine === 'mysql'\n ? (['@prisma/adapter-mariadb', 'mariadb'] as const)\n : (['@prisma/adapter-pg', 'pg'] as const)),\n ]),\n withVersions([\n '@types/aws-lambda',\n ...(options.engine === 'mysql'\n ? ([] as const)\n : (['@types/pg'] as const)),\n ]),\n joinPathFragments(projectConfig.root, 'package.json'),\n );\n // The prisma CLI and tsx run migration/seed scripts from the root.\n addDependenciesToPackageJson(tree, {}, withVersions(['prisma', 'tsx']));\n\n registerPnpmBuiltDependencies(tree, {\n '@prisma/engines': false,\n prisma: false,\n });\n\n await addGeneratorMetricsIfApplicable(tree, [TS_RDB_GENERATOR_INFO]);\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, options.preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\nexport default tsRdbGenerator;\n"],"names":["relative","generateFiles","joinPathFragments","readProjectConfiguration","updateJson","updateProjectConfiguration","addTypeScriptBundleTarget","resolveContainers","addDependenciesToPackageJson","addDockerScanTarget","nodeImageVersions","formatFilesInSubtree","FsCommands","updateGitIgnore","resolveIac","installDependencies","addGeneratorMetricsIfApplicable","esmVars","kebabCase","snakeCase","toClassName","getNpmScope","addDependencyToTargetIfNotPresent","addGeneratorMetadata","getGeneratorInfo","getRelativePathToRootByDirectory","registerPnpmBuiltDependencies","assignPort","addRdbInfra","sharedConstructsGenerator","PACKAGES_DIR","SHARED_SCRIPTS_DIR","sharedRdbScriptsGenerator","TS_VERSIONS","withVersions","tsProjectGenerator","getTsLibDetails","TS_RDB_GENERATOR_INFO","filename","tsRdbGenerator","tree","options","nameKebabCase","name","nameClassName","databaseUser","databaseName","containerEngine","fullyQualifiedName","dir","directory","subDirectory","projectExists","preferInstallDependencies","tsConfig","include","projectConfig","localDbPort","engine","localDbHost","localDbUser","localDbPassword","containerName","dockerImage","templateOptions","runtimeConfigKey","databasePackageAlias","databaseProvider","prismaVersion","prisma","prismaAdapterPackage","prismaAdapterClassName","localDbName","dirname","patterns","waitForDbScript","scriptsDir","relativePathToRoot","root","fs","migrationBundleDir","migrationDockerImageTag","infra","targetFilePath","bundleOutputDir","bundleTarget","targets","commands","rolldownCommand","command","rm","mkdir","cp","parallel","generate","executor","outputs","cwd","continuous","dependsOn","env","LOCAL_DEV","iac","cache","project","trivyTargetName","dockerTargetName","imageTags","projectName","projectRoot","adminUser","createDbUserBundleDir","framework","languages"],"mappings":"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"}
|
|
@@ -11,6 +11,10 @@ terraform {
|
|
|
11
11
|
source = "hashicorp/external"
|
|
12
12
|
version = "<%- externalProviderVersion %>"
|
|
13
13
|
}
|
|
14
|
+
time = {
|
|
15
|
+
source = "hashicorp/time"
|
|
16
|
+
version = "<%- timeProviderVersion %>"
|
|
17
|
+
}
|
|
14
18
|
<%_ } _%>
|
|
15
19
|
random = {
|
|
16
20
|
source = "hashicorp/random"
|
|
@@ -135,6 +139,20 @@ resource "aws_iam_role_policy" "gateway_role_policy" {
|
|
|
135
139
|
], var.additional_iam_policy_statements)
|
|
136
140
|
})
|
|
137
141
|
}
|
|
142
|
+
|
|
143
|
+
# IAM is eventually consistent, and CreateGateway assumes the gateway role to
|
|
144
|
+
# call AuthorizeAction as part of its policy engine check. Without this wait
|
|
145
|
+
# the check intermittently runs before the policy above has propagated and
|
|
146
|
+
# CreateGateway fails with AccessDeniedException.
|
|
147
|
+
resource "time_sleep" "gateway_role_policy_propagation" {
|
|
148
|
+
depends_on = [aws_iam_role_policy.gateway_role_policy]
|
|
149
|
+
|
|
150
|
+
create_duration = "30s"
|
|
151
|
+
|
|
152
|
+
triggers = {
|
|
153
|
+
policy = aws_iam_role_policy.gateway_role_policy.policy
|
|
154
|
+
}
|
|
155
|
+
}
|
|
138
156
|
<%_ } else { _%>
|
|
139
157
|
# Inline policy for statements appended by connection generators
|
|
140
158
|
# (e.g. InvokeAgentRuntime*)
|
|
@@ -190,7 +208,7 @@ resource "aws_bedrockagentcore_gateway" "this" {
|
|
|
190
208
|
mode = "ENFORCE"
|
|
191
209
|
}
|
|
192
210
|
|
|
193
|
-
depends_on = [
|
|
211
|
+
depends_on = [time_sleep.gateway_role_policy_propagation]
|
|
194
212
|
<%_ } _%>
|
|
195
213
|
}
|
|
196
214
|
|
package/src/utils/docker.d.ts
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { type ProjectConfiguration, type Tree } from '@nx/devkit';
|
|
6
|
+
/**
|
|
7
|
+
* Substitution variables for the vended Node `Dockerfile` templates: the npm
|
|
8
|
+
* version to install globally, and the versions its dependency overrides pin.
|
|
9
|
+
*/
|
|
10
|
+
export declare const nodeImageVersions: () => {
|
|
11
|
+
npmVersion: "12.0.1";
|
|
12
|
+
minimatchVersion: "10.2.5";
|
|
13
|
+
};
|
|
6
14
|
export interface DockerScanTargetOptions {
|
|
7
15
|
/**
|
|
8
16
|
* Project configuration to add the scan target to (mutated in place).
|
package/src/utils/docker.js
CHANGED
|
@@ -4,12 +4,19 @@
|
|
|
4
4
|
*/ import { joinPathFragments } from "@nx/devkit";
|
|
5
5
|
import { FsCommands } from "./fs.js";
|
|
6
6
|
import { addDependencyToTargetIfNotPresent } from "./nx.js";
|
|
7
|
-
import { CONTAINER_VERSIONS } from "./versions.js";
|
|
7
|
+
import { CONTAINER_VERSIONS, TS_VERSIONS } from "./versions.js";
|
|
8
8
|
const TRIVY_IGNORE_FILE = '.trivyignore';
|
|
9
9
|
const TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.g. CVE-2021-12345) per line to
|
|
10
10
|
# suppress it during the image scan (\`nx run-many --target trivy\`).
|
|
11
11
|
# https://trivy.dev/latest/docs/configuration/filtering/#by-finding-ids
|
|
12
12
|
`;
|
|
13
|
+
/**
|
|
14
|
+
* Substitution variables for the vended Node `Dockerfile` templates: the npm
|
|
15
|
+
* version to install globally, and the versions its dependency overrides pin.
|
|
16
|
+
*/ export const nodeImageVersions = ()=>({
|
|
17
|
+
npmVersion: TS_VERSIONS.npm,
|
|
18
|
+
minimatchVersion: TS_VERSIONS.minimatch
|
|
19
|
+
});
|
|
13
20
|
/**
|
|
14
21
|
* Add a cacheable Trivy scan target for the images built by a docker target
|
|
15
22
|
* and wire it under the aggregate \`trivy\` target. The scan is not part of
|
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 { FsCommands } from './fs';\nimport { addDependencyToTargetIfNotPresent } from './nx';\nimport { CONTAINER_VERSIONS } from './versions';\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\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 = (\n tree: Tree,\n options: DockerScanTargetOptions,\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 = `public.ecr.aws/aquasecurity/trivy:${CONTAINER_VERSIONS.trivy}`;\n\n const fs = new FsCommands(tree);\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","FsCommands","addDependencyToTargetIfNotPresent","CONTAINER_VERSIONS","TRIVY_IGNORE_FILE","TRIVY_IGNORE_CONTENTS","addDockerScanTarget","tree","options","project","containerEngine","trivyTargetName","dockerTargetName","imageTags","projectRoot","root","ignoreFilePath","exists","write","scanKey","replace","scanDir","trivyImage","trivy","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,SAASC,UAAU,QAAQ,UAAO;AAClC,SAASC,iCAAiC,QAAQ,UAAO;AACzD,SAASC,kBAAkB,QAAQ,gBAAa;
|
|
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 { FsCommands } from './fs';\nimport { addDependencyToTargetIfNotPresent } from './nx';\nimport { CONTAINER_VERSIONS, TS_VERSIONS } from './versions';\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\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 = (\n tree: Tree,\n options: DockerScanTargetOptions,\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 = `public.ecr.aws/aquasecurity/trivy:${CONTAINER_VERSIONS.trivy}`;\n\n const fs = new FsCommands(tree);\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","FsCommands","addDependencyToTargetIfNotPresent","CONTAINER_VERSIONS","TS_VERSIONS","TRIVY_IGNORE_FILE","TRIVY_IGNORE_CONTENTS","nodeImageVersions","npmVersion","npm","minimatchVersion","minimatch","addDockerScanTarget","tree","options","project","containerEngine","trivyTargetName","dockerTargetName","imageTags","projectRoot","root","ignoreFilePath","exists","write","scanKey","replace","scanDir","trivyImage","trivy","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,SAASC,UAAU,QAAQ,UAAO;AAClC,SAASC,iCAAiC,QAAQ,UAAO;AACzD,SAASC,kBAAkB,EAAEC,WAAW,QAAQ,gBAAa;AAE7D,MAAMC,oBAAoB;AAE1B,MAAMC,wBAAwB,CAAC;;;AAG/B,CAAC;AAED;;;CAGC,GACD,OAAO,MAAMC,oBAAoB,IAAO,CAAA;QACtCC,YAAYJ,YAAYK,GAAG;QAC3BC,kBAAkBN,YAAYO,SAAS;IACzC,CAAA,EAAG;AA0BH;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAMC,sBAAsB,CACjCC,MACAC;IAEA,MAAM,EAAEC,OAAO,EAAEC,eAAe,EAAEC,eAAe,EAAEC,gBAAgB,EAAE,GACnEJ;IACF,MAAM,EAAEK,SAAS,EAAE,GAAGL;IACtB,MAAMM,cAAcL,QAAQM,IAAI;IAEhC,MAAMC,iBAAiBtB,kBAAkBoB,aAAaf;IACtD,IAAI,CAACQ,KAAKU,MAAM,CAACD,iBAAiB;QAChCT,KAAKW,KAAK,CAACF,gBAAgBhB;IAC7B;IAEA,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,wDAAwD;IACxD,MAAMmB,UAAUN,SAAS,CAAC,EAAE,CAACO,OAAO,CAAC,kBAAkB;IACvD,MAAMC,UAAU3B,kBAAkB,QAAQoB,aAAa,SAASK;IAChE,MAAMG,aAAa,CAAC,kCAAkC,EAAEzB,mBAAmB0B,KAAK,EAAE;IAElF,MAAMC,KAAK,IAAI7B,WAAWY;IAC1B,MAAMkB,WAAW;QACfD,GAAGE,EAAE,CAACL;QACNG,GAAGG,KAAK,CAACN;QACTG,GAAGI,EAAE,CAACZ,gBAAgBtB,kBAAkB2B,SAAStB;KAClD;IAEDc,UAAUgB,OAAO,CAAC,CAACC,UAAUC;QAC3B,MAAMC,UAAU,CAAC,MAAM,EAAED,MAAM,IAAI,CAAC;QACpCN,SAASQ,IAAI,CACX,GAAGvB,gBAAgB,SAAS,EAAEhB,kBAAkB2B,SAASW,SAAS,CAAC,EAAEF,UAAU,EAC/E,GAAGpB,gBAAgB,gBAAgB,EAAEW,QAAQ,QAAQ,EAAEC,WAAW,qBAAqB,EAAEU,QAAQ,oBAAoB,EAAEjC,kBAAkB,yFAAyF,CAAC;IAEvO;IAEAU,QAAQyB,OAAO,KAAK,CAAC;IACrBzB,QAAQyB,OAAO,CAACvB,gBAAgB,GAAG;QACjCwB,OAAO;QACP,0EAA0E;QAC1E,gEAAgE;QAChEC,QAAQ;YAAC;YAAW;SAAc;QAClCC,SAAS;YAAC,CAAC,gBAAgB,EAAEhB,SAAS;SAAC;QACvCiB,UAAU;QACV9B,SAAS;YACPiB;YACAc,UAAU;QACZ;QACAC,WAAW;YAAC5B;SAAiB;IAC/B;IAEA,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAID,oBAAoB,SAAS;QAC/Bf,kCAAkCa,SAAS,SAASE;IACtD;AACF,EAAE"}
|
package/src/utils/format.js
CHANGED
|
@@ -129,10 +129,14 @@ const BIOME_FORMATTABLE_EXTENSIONS = new Set([
|
|
|
129
129
|
// Resolve each project's ruff settings (module names, line-length) so files
|
|
130
130
|
// are formatted to match the on-disk build (see getPythonProjectRuffConfigs).
|
|
131
131
|
const pythonProjectConfigs = pyFiles.length ? getPythonProjectRuffConfigs(tree) : [];
|
|
132
|
+
// Run ruff from the workspace root so it resolves the same on-disk config
|
|
133
|
+
// hasRuffConfigOnDisk probed for. An in-memory tree has no root on disk, in
|
|
134
|
+
// which case there is no config to find and the process cwd is left alone.
|
|
135
|
+
const ruffCwd = pyFiles.length && existsSync(tree.root) ? tree.root : undefined;
|
|
132
136
|
// Format Python files with ruff (lint fixes + formatting)
|
|
133
137
|
for (const file of pyFiles){
|
|
134
138
|
try {
|
|
135
|
-
const content = ruffFixAndFormat(file.content.toString('utf-8'), file.path, hasRuffConfigOnDisk(tree, file.path), getOwningProjectRuffConfig(file.path, pythonProjectConfigs));
|
|
139
|
+
const content = ruffFixAndFormat(file.content.toString('utf-8'), file.path, hasRuffConfigOnDisk(tree, file.path), ruffCwd, getOwningProjectRuffConfig(file.path, pythonProjectConfigs));
|
|
136
140
|
tree.write(file.path, content);
|
|
137
141
|
} catch {
|
|
138
142
|
// Silently skip ruff formatting failures
|
|
@@ -311,6 +315,19 @@ function getRuffCommand() {
|
|
|
311
315
|
dir = parent;
|
|
312
316
|
}
|
|
313
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* The `[tool.ruff.lint].select` generated Python projects vend. Generation
|
|
320
|
+
* formats before that config lands on disk, so it is pinned here to keep
|
|
321
|
+
* generated files clean under the project's own `lint` target rather than under
|
|
322
|
+
* whatever ruff's defaults happen to be for the pinned release.
|
|
323
|
+
*/ const DEFAULT_RUFF_SELECT = [
|
|
324
|
+
'E',
|
|
325
|
+
'F',
|
|
326
|
+
'UP',
|
|
327
|
+
'B',
|
|
328
|
+
'SIM',
|
|
329
|
+
'I'
|
|
330
|
+
];
|
|
314
331
|
/**
|
|
315
332
|
* Derive ruff's `target-version` (eg `py314`) from a PEP 508
|
|
316
333
|
* `requires-python` specifier (eg `>=3.14`). Ruff targets the minimum
|
|
@@ -335,7 +352,8 @@ function getRuffCommand() {
|
|
|
335
352
|
/**
|
|
336
353
|
* Map each Nx project with a `pyproject.toml` to the ruff settings the on-disk
|
|
337
354
|
* build enforces for it: its top-level module names (from
|
|
338
|
-
* `[tool.hatch.build.targets.wheel].packages`)
|
|
355
|
+
* `[tool.hatch.build.targets.wheel].packages`), its `[tool.ruff].line-length`
|
|
356
|
+
* and its `[tool.ruff.lint].select`.
|
|
339
357
|
*/ function getPythonProjectRuffConfigs(tree) {
|
|
340
358
|
const configs = [];
|
|
341
359
|
for (const project of getProjects(tree).values()){
|
|
@@ -349,12 +367,15 @@ function getRuffCommand() {
|
|
|
349
367
|
const modules = Array.isArray(wheelPackages) ? wheelPackages.filter((pkg)=>typeof pkg === 'string' && !!pkg).map((pkg)=>pkg.split('/')[0]) : [];
|
|
350
368
|
const lineLength = pyproject?.tool?.ruff?.['line-length'];
|
|
351
369
|
const targetVersion = requiresPythonToRuffTarget(pyproject?.project?.['requires-python']);
|
|
352
|
-
|
|
370
|
+
const selected = pyproject?.tool?.ruff?.lint?.select;
|
|
371
|
+
const select = Array.isArray(selected) ? selected.filter((rule)=>typeof rule === 'string' && !!rule) : undefined;
|
|
372
|
+
if (modules.length || typeof lineLength === 'number' || targetVersion || select?.length) {
|
|
353
373
|
configs.push({
|
|
354
374
|
root: project.root.split(path.sep).join('/'),
|
|
355
375
|
modules,
|
|
356
376
|
lineLength: typeof lineLength === 'number' ? lineLength : undefined,
|
|
357
|
-
targetVersion
|
|
377
|
+
targetVersion,
|
|
378
|
+
select: select?.length ? select : undefined
|
|
358
379
|
});
|
|
359
380
|
}
|
|
360
381
|
} catch {
|
|
@@ -384,11 +405,16 @@ function getRuffCommand() {
|
|
|
384
405
|
* Run ruff check --fix and ruff format on Python file content via stdin.
|
|
385
406
|
* Applies all configured lint fixes (including import sorting) and formatting.
|
|
386
407
|
*
|
|
387
|
-
* When no ruff config exists on disk (`hasConfig` false) ruff
|
|
388
|
-
* defaults, which
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
408
|
+
* When no ruff config exists on disk (`hasConfig` false) ruff would fall back to
|
|
409
|
+
* its own defaults, which change between releases — 0.16 widened them from `E`
|
|
410
|
+
* and `F` to 36 rule prefixes — so generation would apply fixes the project's
|
|
411
|
+
* build never asks for (eg `RUF022` reordering `__all__`). Instead run
|
|
412
|
+
* `--isolated` with the project's own `lint.select` pinned, so generation
|
|
413
|
+
* enforces exactly what `lint` does and nothing more. `--isolated` also stops
|
|
414
|
+
* ruff walking above the workspace to a stray config on the host.
|
|
415
|
+
*
|
|
416
|
+
* When a config does exist we defer to it entirely, honouring the user's rule
|
|
417
|
+
* selection, and run from `tree.root` so ruff discovers it.
|
|
392
418
|
*
|
|
393
419
|
* `projectConfig` carries the owning project's ruff settings, which ruff cannot
|
|
394
420
|
* detect from the filesystem during generation because the project lives only
|
|
@@ -397,11 +423,16 @@ function getRuffCommand() {
|
|
|
397
423
|
* imports in their own group, and `line-length` keeps wrapping consistent (the
|
|
398
424
|
* generated config raises it above ruff's default of 88). These are additive to
|
|
399
425
|
* any on-disk config, so they are safe to pass regardless of `hasConfig`.
|
|
400
|
-
*/ function ruffFixAndFormat(content, filePath, hasConfig, projectConfig) {
|
|
426
|
+
*/ function ruffFixAndFormat(content, filePath, hasConfig, cwd, projectConfig) {
|
|
401
427
|
const ruff = getRuffCommand();
|
|
402
428
|
if (!ruff) return content;
|
|
403
|
-
const extendSelect = hasConfig ? '' : ' --extend-select I';
|
|
404
429
|
const configArgs = [];
|
|
430
|
+
// Pin the rule selection only when deferring to ruff's defaults would
|
|
431
|
+
// otherwise apply rules the project's build does not enforce.
|
|
432
|
+
const isolated = !hasConfig;
|
|
433
|
+
if (isolated) {
|
|
434
|
+
configArgs.push(`lint.select = ${JSON.stringify(projectConfig?.select ?? DEFAULT_RUFF_SELECT)}`);
|
|
435
|
+
}
|
|
405
436
|
if (projectConfig?.modules.length) {
|
|
406
437
|
configArgs.push(`lint.isort.known-first-party = ${JSON.stringify(projectConfig.modules)}`);
|
|
407
438
|
}
|
|
@@ -412,17 +443,23 @@ function getRuffCommand() {
|
|
|
412
443
|
configArgs.push(`target-version = "${projectConfig.targetVersion}"`);
|
|
413
444
|
}
|
|
414
445
|
const config = configArgs.map((arg)=>` --config ${JSON.stringify(arg)}`).join('');
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
input
|
|
446
|
+
const flags = `${isolated ? ' --isolated' : ''}${config}`;
|
|
447
|
+
// Built per invocation: `input` carries the content the previous step emitted.
|
|
448
|
+
const options = (input)=>({
|
|
449
|
+
input,
|
|
419
450
|
encoding: 'utf-8',
|
|
420
451
|
stdio: [
|
|
421
452
|
'pipe',
|
|
422
453
|
'pipe',
|
|
423
454
|
'pipe'
|
|
424
|
-
]
|
|
455
|
+
],
|
|
456
|
+
...cwd ? {
|
|
457
|
+
cwd
|
|
458
|
+
} : {}
|
|
425
459
|
});
|
|
460
|
+
// First apply lint fixes (import sorting, unused imports, etc.)
|
|
461
|
+
try {
|
|
462
|
+
const result = execSync(`${ruff} check --fix${flags} --stdin-filename ${filePath} -`, options(content));
|
|
426
463
|
content = result;
|
|
427
464
|
} catch (e) {
|
|
428
465
|
// ruff check exits non-zero when it finds unfixable issues,
|
|
@@ -433,15 +470,7 @@ function getRuffCommand() {
|
|
|
433
470
|
}
|
|
434
471
|
// Then apply formatting
|
|
435
472
|
try {
|
|
436
|
-
content = execSync(`${ruff} format${
|
|
437
|
-
input: content,
|
|
438
|
-
encoding: 'utf-8',
|
|
439
|
-
stdio: [
|
|
440
|
-
'pipe',
|
|
441
|
-
'pipe',
|
|
442
|
-
'pipe'
|
|
443
|
-
]
|
|
444
|
-
});
|
|
473
|
+
content = execSync(`${ruff} format${flags} --stdin-filename ${filePath} -`, options(content));
|
|
445
474
|
} catch {
|
|
446
475
|
// Fall through with whatever content we have
|
|
447
476
|
}
|
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 { execFileSync, execSync } from 'child_process';\nimport { existsSync, readFileSync } from 'fs';\nimport { createRequire } from 'module';\nimport path from 'path';\nimport { uvxCommand } from './py';\nimport { readToml } from './toml';\nimport { TS_VERSIONS } from './versions';\n\nconst require = createRequire(import.meta.url);\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\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): Promise<void> {\n const changedFiles = tree\n .listChanges()\n .filter((file) => file.type !== 'DELETE')\n .filter((file) => (dir ? file.path.startsWith(dir) : true));\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 hasRuffConfigOnDisk(tree, file.path),\n getOwningProjectRuffConfig(file.path, pythonProjectConfigs),\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 // Use the workspace's own Biome CLI (its version and config) when biome.json\n // exists on disk; otherwise format via the bundled library API with the\n // in-memory tree config. The CLI path does not see in-tree config changes.\n if (existsSync(path.join(tree.root, 'biome.json'))) {\n formatWithBiomeCli(tree, otherFiles);\n } else {\n formatWithBiomeApi(tree, otherFiles);\n }\n}\n\n/**\n * Format files via the workspace's Biome CLI, run from the workspace root so it\n * discovers the on-disk biome.json.\n */\nfunction formatWithBiomeCli(\n tree: Tree,\n files: { path: string; content: Buffer | null }[],\n): void {\n const biome = getBiomeCommand(tree.root);\n if (!biome) {\n // Fall back to the library API if the CLI cannot be resolved\n formatWithBiomeApi(tree, files);\n return;\n }\n\n for (const file of files) {\n try {\n const content = execFileSync(\n biome.command,\n [...biome.args, 'format', `--stdin-file-path=${file.path}`],\n {\n input: file.content?.toString('utf-8') ?? '',\n encoding: 'utf-8',\n cwd: tree.root,\n stdio: ['pipe', 'pipe', 'pipe'],\n },\n );\n tree.write(file.path, content);\n } catch {\n // Leave individual files that fail to format untouched\n }\n }\n}\n\n/**\n * Format files via the bundled Biome library API, applying the in-memory tree\n * config.\n */\nfunction formatWithBiomeApi(\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 // Apply the workspace biome.json if it exists in the tree, otherwise the defaults.\n const treeConfig = tree.read('biome.json', 'utf-8');\n biome.applyConfiguration(\n projectKey,\n treeConfig ? JSON.parse(treeConfig) : getDefaultBiomeConfig(tree),\n );\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\ninterface BiomeCommand {\n command: string;\n args: string[];\n}\n\n/**\n * Resolve the `@biomejs/biome` CLI from the user's workspace, falling back to a\n * `biome` binary on the PATH.\n */\nconst _biomeCommands = new Map<string, BiomeCommand | null>();\nfunction getBiomeCommand(root: string): BiomeCommand | undefined {\n if (_biomeCommands.has(root)) {\n return _biomeCommands.get(root) ?? undefined;\n }\n\n // Run via node for cross-platform execution of the bin shim.\n try {\n const pkgJsonPath = require.resolve('@biomejs/biome/package.json', {\n paths: [root, import.meta.dirname],\n });\n const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n const binRelative =\n typeof pkgJson.bin === 'string' ? pkgJson.bin : pkgJson.bin?.biome;\n if (binRelative) {\n const binPath = path.join(path.dirname(pkgJsonPath), binRelative);\n const command = { command: process.execPath, args: [binPath] };\n _biomeCommands.set(root, command);\n return command;\n }\n } catch {\n // Fall back to a biome binary on the PATH\n }\n\n try {\n execSync('biome --version', {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const command = { command: 'biome', args: [] };\n _biomeCommands.set(root, command);\n return command;\n } catch {\n _biomeCommands.set(root, null);\n return undefined;\n }\n}\n\n/**\n * Find the ruff command: `uvx --from ruff==<version> ruff`. uvx works\n * regardless of workspace resolution state (unlike `uv run ruff`, which fails\n * while installs are deferred), and the version pin matches the project's\n * `format` target (PY_VERSIONS) so generation and check format identically.\n * Only a successful probe is cached — ruff can become available mid-run in the\n * long-lived Nx daemon, so a cached failure would skip formatting thereafter.\n */\nlet _ruffCommand: string | undefined;\nfunction getRuffCommand(): string | undefined {\n if (_ruffCommand) {\n return _ruffCommand;\n }\n const cmd = uvxCommand('ruff');\n try {\n execSync(`${cmd} --version`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n _ruffCommand = cmd;\n return cmd;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Whether ruff would discover a config on disk for a file, by walking from its\n * directory up to the workspace root looking for `.ruff.toml`, `ruff.toml`, or a\n * `pyproject.toml` with a `[tool.ruff]` section — the same files ruff itself\n * resolves. The walk stops at `tree.root` so a stray config in a parent of the\n * workspace (or the home directory) is never treated as the project's. Used to\n * decide whether to nudge ruff towards import sorting (see\n * {@link ruffFixAndFormat}).\n */\nfunction hasRuffConfigOnDisk(tree: Tree, filePath: string): boolean {\n const root = path.resolve(tree.root);\n let dir = path.resolve(root, path.dirname(filePath));\n while (true) {\n if (\n existsSync(path.join(dir, '.ruff.toml')) ||\n existsSync(path.join(dir, 'ruff.toml'))\n ) {\n return true;\n }\n const pyproject = path.join(dir, 'pyproject.toml');\n if (\n existsSync(pyproject) &&\n readFileSync(pyproject, 'utf-8').includes('[tool.ruff')\n ) {\n return true;\n }\n const parent = path.dirname(dir);\n // Stop once the workspace root has been checked (or we hit the FS root).\n if (dir === root || parent === dir) {\n return false;\n }\n dir = parent;\n }\n}\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}\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`) and its `[tool.ruff].line-length`.\n */\nfunction getPythonProjectRuffConfigs(tree: Tree): PythonProjectRuffConfig[] {\n const configs: PythonProjectRuffConfig[] = [];\n\n for (const project of getProjects(tree).values()) {\n const pyprojectPath = path.join(project.root, 'pyproject.toml');\n if (tree.exists(pyprojectPath)) {\n try {\n const pyproject = readToml(tree, pyprojectPath) as any;\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 if (modules.length || typeof lineLength === 'number' || targetVersion) {\n configs.push({\n root: project.root.split(path.sep).join('/'),\n modules,\n lineLength: typeof lineLength === 'number' ? lineLength : undefined,\n targetVersion,\n });\n }\n } catch {\n // Skip projects whose pyproject.toml cannot be parsed\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 * Run ruff check --fix and ruff format on Python file content via stdin.\n * Applies all configured lint fixes (including import sorting) and formatting.\n *\n * When no ruff config exists on disk (`hasConfig` false) ruff falls back to its\n * defaults, which omit isort — but generated projects enable rule `I` and their\n * build fails on unsorted imports (I001). In that case we add `--extend-select\n * I` so import sorting matches what the build enforces. When a config does\n * exist we defer to it entirely, honouring the user's rule selection.\n *\n * `projectConfig` carries the owning project's ruff settings, which ruff cannot\n * detect from the filesystem during generation because the project lives only\n * in the tree. We pass them via `--config` so in-tree formatting matches the\n * on-disk build: `known-first-party` (the project's own modules) keeps its\n * imports in their own group, and `line-length` keeps wrapping consistent (the\n * generated config raises it above ruff's default of 88). These are additive to\n * any on-disk config, so they are safe to pass regardless of `hasConfig`.\n */\nfunction ruffFixAndFormat(\n content: string,\n filePath: string,\n hasConfig: boolean,\n projectConfig?: PythonProjectRuffConfig,\n): string {\n const ruff = getRuffCommand();\n if (!ruff) return content;\n\n const extendSelect = hasConfig ? '' : ' --extend-select I';\n const configArgs: string[] = [];\n if (projectConfig?.modules.length) {\n configArgs.push(\n `lint.isort.known-first-party = ${JSON.stringify(projectConfig.modules)}`,\n );\n }\n if (typeof projectConfig?.lineLength === 'number') {\n configArgs.push(`line-length = ${projectConfig.lineLength}`);\n }\n if (projectConfig?.targetVersion) {\n configArgs.push(`target-version = \"${projectConfig.targetVersion}\"`);\n }\n const config = configArgs\n .map((arg) => ` --config ${JSON.stringify(arg)}`)\n .join('');\n\n // First apply lint fixes (import sorting, unused imports, etc.)\n try {\n const result = execSync(\n `${ruff} check --fix${extendSelect}${config} --stdin-filename ${filePath} -`,\n { input: content, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] },\n );\n content = result;\n } catch (e: any) {\n // ruff check exits non-zero when it finds unfixable issues,\n // but stdout still contains the fixed content\n if (e.stdout) {\n content = e.stdout;\n }\n }\n\n // Then apply formatting\n try {\n content = execSync(\n `${ruff} format${config} --stdin-filename ${filePath} -`,\n {\n input: content,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n },\n );\n } catch {\n // Fall through with whatever content we have\n }\n\n return content;\n}\n"],"names":["Biome","getProjects","execFileSync","execSync","existsSync","readFileSync","createRequire","path","uvxCommand","readToml","TS_VERSIONS","require","url","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","formatFilesInSubtree","dir","changedFiles","listChanges","filter","file","type","startsWith","pyFiles","endsWith","otherFiles","has","extname","pythonProjectConfigs","length","getPythonProjectRuffConfigs","content","ruffFixAndFormat","toString","hasRuffConfigOnDisk","getOwningProjectRuffConfig","write","join","formatWithBiomeCli","formatWithBiomeApi","biome","getBiomeCommand","command","args","input","encoding","cwd","stdio","projectKey","openProject","treeConfig","read","applyConfiguration","JSON","parse","formatContent","_biomeCommands","Map","get","undefined","pkgJsonPath","resolve","paths","dirname","pkgJson","binRelative","bin","binPath","process","execPath","set","_ruffCommand","getRuffCommand","cmd","pyproject","parent","requiresPythonToRuffTarget","requiresPython","min","match","matchAll","major","Number","minor","configs","project","values","pyprojectPath","wheelPackages","tool","hatch","build","targets","wheel","packages","modules","Array","isArray","pkg","map","split","lineLength","ruff","targetVersion","push","sep","owner","config","hasConfig","projectConfig","extendSelect","configArgs","stringify","arg","result","e","stdout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/format.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Biome } from '@biomejs/js-api/nodejs';\nimport { getProjects, type Tree } from '@nx/devkit';\nimport {\n type ExecSyncOptionsWithStringEncoding,\n execFileSync,\n execSync,\n} from 'child_process';\nimport { existsSync, readFileSync } from 'fs';\nimport { createRequire } from 'module';\nimport path from 'path';\nimport { uvxCommand } from './py';\nimport { readToml } from './toml';\nimport { TS_VERSIONS } from './versions';\n\nconst require = createRequire(import.meta.url);\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\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): Promise<void> {\n const changedFiles = tree\n .listChanges()\n .filter((file) => file.type !== 'DELETE')\n .filter((file) => (dir ? file.path.startsWith(dir) : true));\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 // Run ruff from the workspace root so it resolves the same on-disk config\n // hasRuffConfigOnDisk probed for. An in-memory tree has no root on disk, in\n // which case there is no config to find and the process cwd is left alone.\n const ruffCwd =\n pyFiles.length && existsSync(tree.root) ? tree.root : undefined;\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 hasRuffConfigOnDisk(tree, file.path),\n ruffCwd,\n getOwningProjectRuffConfig(file.path, pythonProjectConfigs),\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 // Use the workspace's own Biome CLI (its version and config) when biome.json\n // exists on disk; otherwise format via the bundled library API with the\n // in-memory tree config. The CLI path does not see in-tree config changes.\n if (existsSync(path.join(tree.root, 'biome.json'))) {\n formatWithBiomeCli(tree, otherFiles);\n } else {\n formatWithBiomeApi(tree, otherFiles);\n }\n}\n\n/**\n * Format files via the workspace's Biome CLI, run from the workspace root so it\n * discovers the on-disk biome.json.\n */\nfunction formatWithBiomeCli(\n tree: Tree,\n files: { path: string; content: Buffer | null }[],\n): void {\n const biome = getBiomeCommand(tree.root);\n if (!biome) {\n // Fall back to the library API if the CLI cannot be resolved\n formatWithBiomeApi(tree, files);\n return;\n }\n\n for (const file of files) {\n try {\n const content = execFileSync(\n biome.command,\n [...biome.args, 'format', `--stdin-file-path=${file.path}`],\n {\n input: file.content?.toString('utf-8') ?? '',\n encoding: 'utf-8',\n cwd: tree.root,\n stdio: ['pipe', 'pipe', 'pipe'],\n },\n );\n tree.write(file.path, content);\n } catch {\n // Leave individual files that fail to format untouched\n }\n }\n}\n\n/**\n * Format files via the bundled Biome library API, applying the in-memory tree\n * config.\n */\nfunction formatWithBiomeApi(\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 // Apply the workspace biome.json if it exists in the tree, otherwise the defaults.\n const treeConfig = tree.read('biome.json', 'utf-8');\n biome.applyConfiguration(\n projectKey,\n treeConfig ? JSON.parse(treeConfig) : getDefaultBiomeConfig(tree),\n );\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\ninterface BiomeCommand {\n command: string;\n args: string[];\n}\n\n/**\n * Resolve the `@biomejs/biome` CLI from the user's workspace, falling back to a\n * `biome` binary on the PATH.\n */\nconst _biomeCommands = new Map<string, BiomeCommand | null>();\nfunction getBiomeCommand(root: string): BiomeCommand | undefined {\n if (_biomeCommands.has(root)) {\n return _biomeCommands.get(root) ?? undefined;\n }\n\n // Run via node for cross-platform execution of the bin shim.\n try {\n const pkgJsonPath = require.resolve('@biomejs/biome/package.json', {\n paths: [root, import.meta.dirname],\n });\n const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n const binRelative =\n typeof pkgJson.bin === 'string' ? pkgJson.bin : pkgJson.bin?.biome;\n if (binRelative) {\n const binPath = path.join(path.dirname(pkgJsonPath), binRelative);\n const command = { command: process.execPath, args: [binPath] };\n _biomeCommands.set(root, command);\n return command;\n }\n } catch {\n // Fall back to a biome binary on the PATH\n }\n\n try {\n execSync('biome --version', {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const command = { command: 'biome', args: [] };\n _biomeCommands.set(root, command);\n return command;\n } catch {\n _biomeCommands.set(root, null);\n return undefined;\n }\n}\n\n/**\n * Find the ruff command: `uvx --from ruff==<version> ruff`. uvx works\n * regardless of workspace resolution state (unlike `uv run ruff`, which fails\n * while installs are deferred), and the version pin matches the project's\n * `format` target (PY_VERSIONS) so generation and check format identically.\n * Only a successful probe is cached — ruff can become available mid-run in the\n * long-lived Nx daemon, so a cached failure would skip formatting thereafter.\n */\nlet _ruffCommand: string | undefined;\nfunction getRuffCommand(): string | undefined {\n if (_ruffCommand) {\n return _ruffCommand;\n }\n const cmd = uvxCommand('ruff');\n try {\n execSync(`${cmd} --version`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n _ruffCommand = cmd;\n return cmd;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Whether ruff would discover a config on disk for a file, by walking from its\n * directory up to the workspace root looking for `.ruff.toml`, `ruff.toml`, or a\n * `pyproject.toml` with a `[tool.ruff]` section — the same files ruff itself\n * resolves. The walk stops at `tree.root` so a stray config in a parent of the\n * workspace (or the home directory) is never treated as the project's. Used to\n * decide whether to nudge ruff towards import sorting (see\n * {@link ruffFixAndFormat}).\n */\nfunction hasRuffConfigOnDisk(tree: Tree, filePath: string): boolean {\n const root = path.resolve(tree.root);\n let dir = path.resolve(root, path.dirname(filePath));\n while (true) {\n if (\n existsSync(path.join(dir, '.ruff.toml')) ||\n existsSync(path.join(dir, 'ruff.toml'))\n ) {\n return true;\n }\n const pyproject = path.join(dir, 'pyproject.toml');\n if (\n existsSync(pyproject) &&\n readFileSync(pyproject, 'utf-8').includes('[tool.ruff')\n ) {\n return true;\n }\n const parent = path.dirname(dir);\n // Stop once the workspace root has been checked (or we hit the FS root).\n if (dir === root || parent === dir) {\n return false;\n }\n dir = parent;\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 const pyprojectPath = path.join(project.root, 'pyproject.toml');\n if (tree.exists(pyprojectPath)) {\n try {\n const pyproject = readToml(tree, pyprojectPath) as any;\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 } catch {\n // Skip projects whose pyproject.toml cannot be parsed\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 * Run ruff check --fix and ruff format on Python file content via stdin.\n * Applies all configured lint fixes (including import sorting) and formatting.\n *\n * When no ruff config exists on disk (`hasConfig` false) ruff would fall back to\n * its own defaults, which change between releases — 0.16 widened them from `E`\n * and `F` to 36 rule prefixes — so generation would apply fixes the project's\n * build never asks for (eg `RUF022` reordering `__all__`). Instead run\n * `--isolated` with the project's own `lint.select` pinned, so generation\n * enforces exactly what `lint` does and nothing more. `--isolated` also stops\n * ruff walking above the workspace to a stray config on the host.\n *\n * When a config does exist we defer to it entirely, honouring the user's rule\n * selection, and run from `tree.root` so ruff discovers it.\n *\n * `projectConfig` carries the owning project's ruff settings, which ruff cannot\n * detect from the filesystem during generation because the project lives only\n * in the tree. We pass them via `--config` so in-tree formatting matches the\n * on-disk build: `known-first-party` (the project's own modules) keeps its\n * imports in their own group, and `line-length` keeps wrapping consistent (the\n * generated config raises it above ruff's default of 88). These are additive to\n * any on-disk config, so they are safe to pass regardless of `hasConfig`.\n */\nfunction ruffFixAndFormat(\n content: string,\n filePath: string,\n hasConfig: boolean,\n cwd: string | undefined,\n projectConfig?: PythonProjectRuffConfig,\n): string {\n const ruff = getRuffCommand();\n if (!ruff) return content;\n\n const configArgs: string[] = [];\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 const isolated = !hasConfig;\n if (isolated) {\n configArgs.push(\n `lint.select = ${JSON.stringify(projectConfig?.select ?? DEFAULT_RUFF_SELECT)}`,\n );\n }\n if (projectConfig?.modules.length) {\n configArgs.push(\n `lint.isort.known-first-party = ${JSON.stringify(projectConfig.modules)}`,\n );\n }\n if (typeof projectConfig?.lineLength === 'number') {\n configArgs.push(`line-length = ${projectConfig.lineLength}`);\n }\n if (projectConfig?.targetVersion) {\n configArgs.push(`target-version = \"${projectConfig.targetVersion}\"`);\n }\n const config = configArgs\n .map((arg) => ` --config ${JSON.stringify(arg)}`)\n .join('');\n const flags = `${isolated ? ' --isolated' : ''}${config}`;\n // Built per invocation: `input` carries the content the previous step emitted.\n const options = (input: string): ExecSyncOptionsWithStringEncoding => ({\n input,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n ...(cwd ? { cwd } : {}),\n });\n\n // First apply lint fixes (import sorting, unused imports, etc.)\n try {\n const result = execSync(\n `${ruff} check --fix${flags} --stdin-filename ${filePath} -`,\n options(content),\n );\n content = result;\n } catch (e: any) {\n // ruff check exits non-zero when it finds unfixable issues,\n // but stdout still contains the fixed content\n if (e.stdout) {\n content = e.stdout;\n }\n }\n\n // Then apply formatting\n try {\n content = execSync(\n `${ruff} format${flags} --stdin-filename ${filePath} -`,\n options(content),\n );\n } catch {\n // Fall through with whatever content we have\n }\n\n return content;\n}\n"],"names":["Biome","getProjects","execFileSync","execSync","existsSync","readFileSync","createRequire","path","uvxCommand","readToml","TS_VERSIONS","require","url","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","formatFilesInSubtree","dir","changedFiles","listChanges","filter","file","type","startsWith","pyFiles","endsWith","otherFiles","has","extname","pythonProjectConfigs","length","getPythonProjectRuffConfigs","ruffCwd","undefined","content","ruffFixAndFormat","toString","hasRuffConfigOnDisk","getOwningProjectRuffConfig","write","join","formatWithBiomeCli","formatWithBiomeApi","biome","getBiomeCommand","command","args","input","encoding","cwd","stdio","projectKey","openProject","treeConfig","read","applyConfiguration","JSON","parse","formatContent","_biomeCommands","Map","get","pkgJsonPath","resolve","paths","dirname","pkgJson","binRelative","bin","binPath","process","execPath","set","_ruffCommand","getRuffCommand","cmd","pyproject","parent","DEFAULT_RUFF_SELECT","requiresPythonToRuffTarget","requiresPython","min","match","matchAll","major","Number","minor","configs","project","values","pyprojectPath","wheelPackages","tool","hatch","build","targets","wheel","packages","modules","Array","isArray","pkg","map","split","lineLength","ruff","targetVersion","selected","lint","select","rule","push","sep","owner","config","hasConfig","projectConfig","configArgs","isolated","stringify","arg","flags","options","result","e","stdout"],"mappings":"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"}
|
package/src/utils/init.js
CHANGED
|
@@ -16,7 +16,7 @@ import { addDependenciesToPackageJson, detectWorkspacePackageManager } from "./d
|
|
|
16
16
|
import { getDefaultBiomeConfig } from "./format.js";
|
|
17
17
|
import { configureMcpServers } from "./mcp.js";
|
|
18
18
|
import { getNpmScope } from "./npm-scope.js";
|
|
19
|
-
import { mergeTargetDefault } from "./nx.js";
|
|
19
|
+
import { mergeTargetDefault, nxPluginMcpDependency, nxPluginSelfDependency } from "./nx.js";
|
|
20
20
|
import { getPackageManagerDisplayCommands } from "./pkg-manager.js";
|
|
21
21
|
import { workspaceGlobs } from "./project-package-json.js";
|
|
22
22
|
import { withVersions } from "./versions.js";
|
|
@@ -246,7 +246,12 @@ export const PNPM_BUILT_DEPENDENCIES = [
|
|
|
246
246
|
...withVersions([
|
|
247
247
|
'typescript',
|
|
248
248
|
'@biomejs/biome'
|
|
249
|
-
])
|
|
249
|
+
]),
|
|
250
|
+
// Declare the plugin the generators are running from, plus the MCP server
|
|
251
|
+
// package the vended config runs, so both are pinned in the workspace's
|
|
252
|
+
// lockfile and upgrading them upgrades what the agents use.
|
|
253
|
+
...nxPluginSelfDependency(tree),
|
|
254
|
+
...nxPluginMcpDependency(tree)
|
|
250
255
|
});
|
|
251
256
|
// Write biome.json for formatting and linting
|
|
252
257
|
if (!tree.exists('biome.json')) {
|