diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 216794a5366..f2953f73fa0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,6 +76,10 @@ jobs: run: node repo-scripts/repo-toolbox/lib/start.js readme --verify working-directory: repo-a + - name: Collect JSON schemas + run: node repo-scripts/repo-toolbox/lib/start.js collect-json-schemas --output-path ${GITHUB_WORKSPACE}/artifacts/json-schemas + working-directory: repo-a + - name: Clone another copy of the repo to test the build cache uses: actions/checkout@v3 with: diff --git a/apps/api-documenter/config/heft.json b/apps/api-documenter/config/heft.json new file mode 100644 index 00000000000..b3046cad172 --- /dev/null +++ b/apps/api-documenter/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/api-extractor/v7"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/apps/api-extractor/config/heft.json b/apps/api-extractor/config/heft.json index 0b965cec36a..a36ac2cf4c0 100644 --- a/apps/api-extractor/config/heft.json +++ b/apps/api-extractor/config/heft.json @@ -29,6 +29,23 @@ ] } } + }, + + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/api-extractor/v7"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } } } } diff --git a/apps/heft/config/heft.json b/apps/heft/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/apps/heft/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/apps/lockfile-explorer/config/heft.json b/apps/lockfile-explorer/config/heft.json index 6664d95480c..ca7a8305599 100644 --- a/apps/lockfile-explorer/config/heft.json +++ b/apps/lockfile-explorer/config/heft.json @@ -58,9 +58,27 @@ } } }, + "typescript": { // The "typescript" task should not run until after "pre-compile-copy" completes. "taskDependencies": ["pre-compile-copy"] + }, + + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/lockfile-explorer"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } } } } diff --git a/common/changes/@microsoft/api-documenter/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@microsoft/api-documenter/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..771f7e84c78 --- /dev/null +++ b/common/changes/@microsoft/api-documenter/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@microsoft/api-documenter" + } + ], + "packageName": "@microsoft/api-documenter", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/api-extractor/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@microsoft/api-extractor/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..f7c3a8a84e4 --- /dev/null +++ b/common/changes/@microsoft/api-extractor/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@microsoft/api-extractor" + } + ], + "packageName": "@microsoft/api-extractor", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/rush/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@microsoft/rush/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..efcd84c45fb --- /dev/null +++ b/common/changes/@microsoft/rush/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@microsoft/rush" + } + ], + "packageName": "@microsoft/rush", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/credential-cache/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/credential-cache/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..540ab06fe41 --- /dev/null +++ b/common/changes/@rushstack/credential-cache/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/credential-cache" + } + ], + "packageName": "@rushstack/credential-cache", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-api-extractor-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-api-extractor-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..aeb8de3babe --- /dev/null +++ b/common/changes/@rushstack/heft-api-extractor-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-api-extractor-plugin" + } + ], + "packageName": "@rushstack/heft-api-extractor-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-isolated-typescript-transpile-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-isolated-typescript-transpile-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..6e97e4a5cf0 --- /dev/null +++ b/common/changes/@rushstack/heft-isolated-typescript-transpile-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-isolated-typescript-transpile-plugin" + } + ], + "packageName": "@rushstack/heft-isolated-typescript-transpile-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-jest-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-jest-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..5175001a683 --- /dev/null +++ b/common/changes/@rushstack/heft-jest-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-jest-plugin" + } + ], + "packageName": "@rushstack/heft-jest-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-json-schema-typings-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-json-schema-typings-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..dffdbf5ca8a --- /dev/null +++ b/common/changes/@rushstack/heft-json-schema-typings-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-json-schema-typings-plugin" + } + ], + "packageName": "@rushstack/heft-json-schema-typings-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-lint-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-lint-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..29f880f4781 --- /dev/null +++ b/common/changes/@rushstack/heft-lint-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-lint-plugin" + } + ], + "packageName": "@rushstack/heft-lint-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-localization-typings-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-localization-typings-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..44efda50839 --- /dev/null +++ b/common/changes/@rushstack/heft-localization-typings-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-localization-typings-plugin" + } + ], + "packageName": "@rushstack/heft-localization-typings-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-sass-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-sass-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..9b91da609ca --- /dev/null +++ b/common/changes/@rushstack/heft-sass-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-sass-plugin" + } + ], + "packageName": "@rushstack/heft-sass-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-storybook-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-storybook-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..e4683c353d5 --- /dev/null +++ b/common/changes/@rushstack/heft-storybook-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-storybook-plugin" + } + ], + "packageName": "@rushstack/heft-storybook-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-typescript-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-typescript-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..4a8e9132357 --- /dev/null +++ b/common/changes/@rushstack/heft-typescript-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-typescript-plugin" + } + ], + "packageName": "@rushstack/heft-typescript-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-webpack4-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-webpack4-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..93df4fb8f95 --- /dev/null +++ b/common/changes/@rushstack/heft-webpack4-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-webpack4-plugin" + } + ], + "packageName": "@rushstack/heft-webpack4-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-webpack5-plugin/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft-webpack5-plugin/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..9340dce0c15 --- /dev/null +++ b/common/changes/@rushstack/heft-webpack5-plugin/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-webpack5-plugin" + } + ], + "packageName": "@rushstack/heft-webpack5-plugin", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/heft/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..ef525830e37 --- /dev/null +++ b/common/changes/@rushstack/heft/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft" + } + ], + "packageName": "@rushstack/heft", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/lockfile-explorer/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/lockfile-explorer/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..6db4dbd541c --- /dev/null +++ b/common/changes/@rushstack/lockfile-explorer/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/lockfile-explorer" + } + ], + "packageName": "@rushstack/lockfile-explorer", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/json-schemas-artifact_2025-11-04-02-46.json b/common/changes/@rushstack/rig-package/json-schemas-artifact_2025-11-04-02-46.json new file mode 100644 index 00000000000..c66505525a1 --- /dev/null +++ b/common/changes/@rushstack/rig-package/json-schemas-artifact_2025-11-04-02-46.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/rig-package" + } + ], + "packageName": "@rushstack/rig-package", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/config/azure-pipelines/npm-publish-rush.yaml b/common/config/azure-pipelines/npm-publish-rush.yaml index 4ac6559321a..972e87a50e0 100644 --- a/common/config/azure-pipelines/npm-publish-rush.yaml +++ b/common/config/azure-pipelines/npm-publish-rush.yaml @@ -29,6 +29,9 @@ extends: - output: pipelineArtifact targetPath: $(Build.ArtifactStagingDirectory)/published-versions artifactName: published-versions + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/json-schemas + artifactName: json-schemas steps: - checkout: self persistCredentials: true @@ -60,4 +63,4 @@ extends: VersionPolicyName: rush BranchName: $(SourceBranch) - - template: /common/config/azure-pipelines/templates/record-published-versions.yaml@self + - template: /common/config/azure-pipelines/templates/post-publish.yaml diff --git a/common/config/azure-pipelines/npm-publish.yaml b/common/config/azure-pipelines/npm-publish.yaml index e71295fae66..a7668d7da7a 100644 --- a/common/config/azure-pipelines/npm-publish.yaml +++ b/common/config/azure-pipelines/npm-publish.yaml @@ -29,6 +29,9 @@ extends: - output: pipelineArtifact targetPath: $(Build.ArtifactStagingDirectory)/published-versions artifactName: published-versions + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/json-schemas + artifactName: json-schemas steps: - checkout: self persistCredentials: true @@ -50,4 +53,4 @@ extends: VersionPolicyName: noRush BranchName: $(SourceBranch) - - template: /common/config/azure-pipelines/templates/record-published-versions.yaml@self + - template: /common/config/azure-pipelines/templates/post-publish.yaml diff --git a/common/config/azure-pipelines/templates/record-published-versions.yaml b/common/config/azure-pipelines/templates/post-publish.yaml similarity index 51% rename from common/config/azure-pipelines/templates/record-published-versions.yaml rename to common/config/azure-pipelines/templates/post-publish.yaml index 9f234db9f8b..ecf0da731d2 100644 --- a/common/config/azure-pipelines/templates/record-published-versions.yaml +++ b/common/config/azure-pipelines/templates/post-publish.yaml @@ -1,7 +1,5 @@ -steps: - - script: 'node repo-scripts/repo-toolbox/lib/start.js record-versions --out-file $(Build.ArtifactStagingDirectory)/published-versions/published-versions.json' - displayName: 'Record Published Versions' - # Published by the 1ES template - # - publish: $(Build.ArtifactStagingDirectory)/published-versions - # artifact: published-versions - # displayName: 'Publish Artifact: published-versions' +steps: + - script: 'node repo-scripts/repo-toolbox/lib/start.js record-versions --out-file $(Build.ArtifactStagingDirectory)/published-versions/published-versions.json' + displayName: 'Record Published Versions' + - script: 'node repo-scripts/repo-toolbox/lib/start.js collect-json-schemas --output-path $(Build.ArtifactStagingDirectory)/json-schemas' + displayName: 'Collect JSON Schemas' diff --git a/heft-plugins/heft-api-extractor-plugin/config/heft.json b/heft-plugins/heft-api-extractor-plugin/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/heft-plugins/heft-api-extractor-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/config/heft.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-jest-plugin/config/heft.json b/heft-plugins/heft-jest-plugin/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/heft-plugins/heft-jest-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/config/heft.json b/heft-plugins/heft-json-schema-typings-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/heft-plugin.json b/heft-plugins/heft-json-schema-typings-plugin/heft-plugin.json index 29eec2267b7..f9db17a5dbd 100644 --- a/heft-plugins/heft-json-schema-typings-plugin/heft-plugin.json +++ b/heft-plugins/heft-json-schema-typings-plugin/heft-plugin.json @@ -5,7 +5,7 @@ { "pluginName": "json-schema-typings-plugin", "entryPoint": "./lib/JsonSchemaTypingsPlugin", - "optionsSchema": "./lib/schemas/options.schema.json" + "optionsSchema": "./lib/schemas/heft-json-schema-typings-plugin.schema.json" } ] } diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/schemas/options.schema.json b/heft-plugins/heft-json-schema-typings-plugin/src/schemas/heft-json-schema-typings-plugin.schema.json similarity index 100% rename from heft-plugins/heft-json-schema-typings-plugin/src/schemas/options.schema.json rename to heft-plugins/heft-json-schema-typings-plugin/src/schemas/heft-json-schema-typings-plugin.schema.json diff --git a/heft-plugins/heft-lint-plugin/config/heft.json b/heft-plugins/heft-lint-plugin/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/heft-plugins/heft-lint-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-localization-typings-plugin/config/heft.json b/heft-plugins/heft-localization-typings-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-localization-typings-plugin/heft-plugin.json b/heft-plugins/heft-localization-typings-plugin/heft-plugin.json index f4a6d738ecf..3d1d82ac7f3 100644 --- a/heft-plugins/heft-localization-typings-plugin/heft-plugin.json +++ b/heft-plugins/heft-localization-typings-plugin/heft-plugin.json @@ -5,7 +5,7 @@ { "pluginName": "localization-typings-plugin", "entryPoint": "./lib/LocalizationTypingsPlugin", - "optionsSchema": "./lib/schemas/options.schema.json" + "optionsSchema": "./lib/schemas/heft-localization-typings-plugin.schema.json" } ] } diff --git a/heft-plugins/heft-localization-typings-plugin/src/schemas/options.schema.json b/heft-plugins/heft-localization-typings-plugin/src/schemas/heft-localization-typings-plugin.schema.json similarity index 100% rename from heft-plugins/heft-localization-typings-plugin/src/schemas/options.schema.json rename to heft-plugins/heft-localization-typings-plugin/src/schemas/heft-localization-typings-plugin.schema.json diff --git a/heft-plugins/heft-rspack-plugin/config/heft.json b/heft-plugins/heft-rspack-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-sass-plugin/config/heft.json b/heft-plugins/heft-sass-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-sass-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-storybook-plugin/config/heft.json b/heft-plugins/heft-storybook-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-storybook-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-typescript-plugin/config/heft.json b/heft-plugins/heft-typescript-plugin/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/heft-plugins/heft-typescript-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-webpack4-plugin/config/heft.json b/heft-plugins/heft-webpack4-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-webpack4-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-webpack5-plugin/config/heft.json b/heft-plugins/heft-webpack5-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-webpack5-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/libraries/credential-cache/config/heft.json b/libraries/credential-cache/config/heft.json new file mode 100644 index 00000000000..0922361a39e --- /dev/null +++ b/libraries/credential-cache/config/heft.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/rush/v5"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/libraries/rig-package/config/heft.json b/libraries/rig-package/config/heft.json new file mode 100644 index 00000000000..2fc245813e5 --- /dev/null +++ b/libraries/rig-package/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/rig-package"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/libraries/rush-lib/config/heft.json b/libraries/rush-lib/config/heft.json index cb203be79e0..e260d7e35e9 100644 --- a/libraries/rush-lib/config/heft.json +++ b/libraries/rush-lib/config/heft.json @@ -63,6 +63,23 @@ "taskPlugin": { "pluginPackage": "@rushstack/heft-webpack5-plugin" } + }, + + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/rush/v5"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } } } } diff --git a/repo-scripts/repo-toolbox/src/cli/ToolboxCommandLine.ts b/repo-scripts/repo-toolbox/src/cli/ToolboxCommandLine.ts index eb384cdb761..aeadc6150cc 100644 --- a/repo-scripts/repo-toolbox/src/cli/ToolboxCommandLine.ts +++ b/repo-scripts/repo-toolbox/src/cli/ToolboxCommandLine.ts @@ -2,10 +2,12 @@ // See LICENSE in the project root for license information. import { CommandLineParser } from '@rushstack/ts-command-line'; +import { ConsoleTerminalProvider, type ITerminal, Terminal } from '@rushstack/terminal'; import { ReadmeAction } from './actions/ReadmeAction'; import { RecordVersionsAction } from './actions/RecordVersionsAction'; import { BumpDecoupledLocalDependencies } from './actions/BumpDecoupledLocalDependencies'; +import { CollectJsonSchemasAction } from './actions/CollectJsonSchemasAction'; export class ToolboxCommandLine extends CommandLineParser { public constructor() { @@ -14,8 +16,11 @@ export class ToolboxCommandLine extends CommandLineParser { toolDescription: 'Used to execute various operations specific to this repo' }); - this.addAction(new ReadmeAction()); - this.addAction(new RecordVersionsAction()); - this.addAction(new BumpDecoupledLocalDependencies()); + const terminal: ITerminal = new Terminal(new ConsoleTerminalProvider()); + + this.addAction(new ReadmeAction(terminal)); + this.addAction(new RecordVersionsAction(terminal)); + this.addAction(new BumpDecoupledLocalDependencies(terminal)); + this.addAction(new CollectJsonSchemasAction(terminal)); } } diff --git a/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts b/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts index b2f10b83de8..7d60e1e775f 100644 --- a/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts +++ b/repo-scripts/repo-toolbox/src/cli/actions/BumpDecoupledLocalDependencies.ts @@ -4,21 +4,25 @@ import type { ChildProcess } from 'node:child_process'; import { Async, Executable, JsonFile } from '@rushstack/node-core-library'; -import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; +import type { ITerminal } from '@rushstack/terminal'; import { DependencyType, RushConfiguration, type CommonVersionsConfiguration } from '@microsoft/rush-lib'; import { CommandLineAction } from '@rushstack/ts-command-line'; export class BumpDecoupledLocalDependencies extends CommandLineAction { - public constructor() { + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { super({ actionName: 'bump-decoupled-local-dependencies', summary: 'Updates decoupled local dependencies inside the repo.', documentation: '' }); + + this._terminal = terminal; } protected override async onExecuteAsync(): Promise { - const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); + const terminal: ITerminal = this._terminal; const rushConfiguration: RushConfiguration = RushConfiguration.loadFromDefaultLocation({ startingFolder: process.cwd() }); @@ -93,7 +97,7 @@ export class BumpDecoupledLocalDependencies extends CommandLineAction { terminal.writeLine(`Updated ${rushConfiguration.rushJsonFile}`); } - private async _getLatestPublishedVersionAsync(terminal: Terminal, packageName: string): Promise { + private async _getLatestPublishedVersionAsync(terminal: ITerminal, packageName: string): Promise { return await new Promise((resolve: (result: string) => void, reject: (error: Error) => void) => { const childProcess: ChildProcess = Executable.spawn('npm', ['view', packageName, 'version'], { stdio: ['ignore', 'pipe', 'pipe'] diff --git a/repo-scripts/repo-toolbox/src/cli/actions/CollectJsonSchemasAction.ts b/repo-scripts/repo-toolbox/src/cli/actions/CollectJsonSchemasAction.ts new file mode 100644 index 00000000000..cdf38cb377a --- /dev/null +++ b/repo-scripts/repo-toolbox/src/cli/actions/CollectJsonSchemasAction.ts @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { FileSystem, AlreadyReportedError, Async, type FolderItem } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import { RushConfiguration, type RushConfigurationProject } from '@microsoft/rush-lib'; +import { CommandLineAction, type IRequiredCommandLineStringParameter } from '@rushstack/ts-command-line'; + +interface IFolderItemToCopy { + absolutePath: string; + relativePath: string; + content: string; +} + +async function* _getFolderItemsRecursiveAsync( + folderAbsolutePath: string, + folderRelativePath: string = '' +): AsyncIterable { + let folderItems: FolderItem[]; + try { + folderItems = await FileSystem.readFolderItemsAsync(folderAbsolutePath); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } else { + return; + } + } + + for (const entry of folderItems) { + const entryRelativePath: string = `${folderRelativePath}/${entry.name}`; + const entryAbsolutePath: string = `${folderAbsolutePath}/${entry.name}`; + if (entry.isDirectory()) { + yield* _getFolderItemsRecursiveAsync(entryAbsolutePath, entryRelativePath); + } else { + const content: string = await FileSystem.readFileAsync(entryAbsolutePath); + yield { + absolutePath: entryAbsolutePath, + relativePath: entryRelativePath, + content + }; + } + } +} + +export class CollectJsonSchemasAction extends CommandLineAction { + private readonly _outputPathParameter: IRequiredCommandLineStringParameter; + + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + super({ + actionName: 'collect-json-schemas', + summary: 'Generates JSON schema files based on rush.json inventory', + documentation: "Use this to update the repo's JSON schema files" + }); + + this._terminal = terminal; + + this._outputPathParameter = this.defineStringParameter({ + parameterLongName: '--output-path', + description: 'Path to the output directory for the generated JSON schema files.', + argumentName: 'PATH', + required: true + }); + } + + protected override async onExecuteAsync(): Promise { + const terminal: ITerminal = this._terminal; + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromDefaultLocation(); + + const outputPath: string = path.resolve(this._outputPathParameter.value); + + const contentByAbsolutePathByRelativePath: Map> = new Map(); + + await Async.forEachAsync( + rushConfiguration.projects, + async ({ projectFolder }: RushConfigurationProject) => { + const schemaFiles: AsyncIterable = _getFolderItemsRecursiveAsync( + `${projectFolder}/temp/json-schemas`, + '' + ); + await Async.forEachAsync(schemaFiles, async ({ absolutePath, relativePath, content }) => { + let contentByAbsolutePath: Map | undefined = + contentByAbsolutePathByRelativePath.get(relativePath); + if (!contentByAbsolutePath) { + contentByAbsolutePath = new Map(); + contentByAbsolutePathByRelativePath.set(relativePath, contentByAbsolutePath); + } + + let absolutePaths: string[] | undefined = contentByAbsolutePath.get(content); + if (!absolutePaths) { + absolutePaths = []; + contentByAbsolutePath.set(content, absolutePaths); + } + + absolutePaths.push(absolutePath); + }, { concurrency: 5 }); + }, + { concurrency: 5 } + ); + + let encounteredCollisions: boolean = false; + const filesToWrite: Map = new Map(); + for (const [relativePath, contentByAbsolutePath] of contentByAbsolutePathByRelativePath) { + if (contentByAbsolutePath.size > 1) { + encounteredCollisions = true; + + terminal.writeErrorLine( + `Multiple projects generated different contents for the JSON schema "${relativePath}":` + ); + + for (const absolutePaths of contentByAbsolutePath.values()) { + for (const absolutePath of absolutePaths) { + terminal.writeErrorLine(` - ${absolutePath}`); + } + } + } else { + filesToWrite.set(`${outputPath}/${relativePath}`, Array.from(contentByAbsolutePath.keys())[0]); + } + } + + if (encounteredCollisions) { + throw new AlreadyReportedError(); + } else { + await FileSystem.ensureEmptyFolderAsync(outputPath); + + await Async.forEachAsync( + filesToWrite, + async ([outPath, content]) => + await FileSystem.writeFileAsync(outPath, content, { ensureFolderExists: true }), + { concurrency: 25 } + ); + } + } +} diff --git a/repo-scripts/repo-toolbox/src/cli/actions/ReadmeAction.ts b/repo-scripts/repo-toolbox/src/cli/actions/ReadmeAction.ts index 01961880ea3..5128ceebf59 100644 --- a/repo-scripts/repo-toolbox/src/cli/actions/ReadmeAction.ts +++ b/repo-scripts/repo-toolbox/src/cli/actions/ReadmeAction.ts @@ -6,7 +6,7 @@ import * as path from 'node:path'; import * as Diff from 'diff'; import { StringBuilder, Sort, FileSystem, Text, AlreadyReportedError } from '@rushstack/node-core-library'; -import { Terminal, ConsoleTerminalProvider, Colorize } from '@rushstack/terminal'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; import { RushConfiguration, type RushConfigurationProject, LockStepVersionPolicy } from '@microsoft/rush-lib'; import { CommandLineAction, type CommandLineFlagParameter } from '@rushstack/ts-command-line'; @@ -16,13 +16,17 @@ const GENERATED_PROJECT_SUMMARY_END_COMMENT_TEXT: string = '