diff --git a/.eslintrc.js b/.eslintrc.js index d9331feb09..e44635f50d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -60,7 +60,7 @@ module.exports = { ], 'rules': { '@o3r/json-dependency-versions-harmonize': ['error', { - ignoredPackages: ['@o3r/build-helpers'], + ignoredPackages: ['@o3r/build-helpers', '@o3r/workspace-helpers'], alignPeerDependencies: false, alignEngines: true }], @@ -81,7 +81,7 @@ module.exports = { ], 'rules': { '@o3r/json-dependency-versions-harmonize': ['error', { - ignoredPackages: ['@o3r/build-helpers'], + ignoredPackages: ['@o3r/build-helpers', '@o3r/workspace-helpers'], ignoredDependencies: ['npm'], alignPeerDependencies: false, alignEngines: true diff --git a/.github/.pr-labelrc.json b/.github/.pr-labelrc.json index 7d2992201d..70a7e02f5f 100644 --- a/.github/.pr-labelrc.json +++ b/.github/.pr-labelrc.json @@ -1,5 +1,5 @@ { - "$schema": "../tools/@o3r/build-helpers/schemas/pr-labels.configuration.schema.json", + "$schema": "../tools/@o3r/workspace-helpers/schemas/pr-labels.configuration.schema.json", "projectLabelPrefix": "project:", "ignoredProjects": [], "ignoreProjectForLabels": ["cascading"] diff --git a/apps/showcase/package.json b/apps/showcase/package.json index 0667f2cea1..a70dab4dbf 100644 --- a/apps/showcase/package.json +++ b/apps/showcase/package.json @@ -97,6 +97,7 @@ "@angular/cli": "~18.2.0", "@angular/compiler-cli": "~18.2.0", "@nx/eslint-plugin": "~19.5.0", + "@o3r/build-helpers": "workspace:^", "@o3r/design": "workspace:^", "@o3r/eslint-config-otter": "workspace:^", "@o3r/eslint-plugin": "workspace:^", diff --git a/package.json b/package.json index c16a9e3d20..1242ca2d03 100644 --- a/package.json +++ b/package.json @@ -193,11 +193,11 @@ "@nx/jest": "~19.5.0", "@nx/js": "~19.5.0", "@nx/workspace": "~19.5.0", - "@o3r/build-helpers": "workspace:^", "@o3r/eslint-config-otter": "workspace:^", "@o3r/eslint-plugin": "workspace:^", "@o3r/telemetry": "workspace:^", "@o3r/workspace": "workspace:^", + "@o3r/workspace-helpers": "workspace:^", "@playwright/test": "~1.47.0", "@popperjs/core": "^2.11.5", "@schematics/angular": "~18.2.0", diff --git a/packages/@ama-sdk/create/.eslintrc.js b/packages/@ama-sdk/create/.eslintrc.js index 4a5eef7ed1..ddfa032ba5 100644 --- a/packages/@ama-sdk/create/.eslintrc.js +++ b/packages/@ama-sdk/create/.eslintrc.js @@ -20,7 +20,7 @@ module.exports = { 'files': ['package.json'], 'rules': { '@o3r/json-dependency-versions-harmonize': ['error', { - 'ignoredPackages': ['@o3r/build-helpers'], + 'ignoredPackages': ['@o3r/build-helpers', '@o3r/workspace-helpers'], ignoredDependencies: ['yarn'], 'alignPeerDependencies': false }] diff --git a/packages/@o3r/create/.eslintrc.js b/packages/@o3r/create/.eslintrc.js index 4a5eef7ed1..ddfa032ba5 100644 --- a/packages/@o3r/create/.eslintrc.js +++ b/packages/@o3r/create/.eslintrc.js @@ -20,7 +20,7 @@ module.exports = { 'files': ['package.json'], 'rules': { '@o3r/json-dependency-versions-harmonize': ['error', { - 'ignoredPackages': ['@o3r/build-helpers'], + 'ignoredPackages': ['@o3r/build-helpers', '@o3r/workspace-helpers'], ignoredDependencies: ['yarn'], 'alignPeerDependencies': false }] diff --git a/packages/@o3r/telemetry/package.json b/packages/@o3r/telemetry/package.json index 8496614dea..b0b2aef8ff 100644 --- a/packages/@o3r/telemetry/package.json +++ b/packages/@o3r/telemetry/package.json @@ -47,6 +47,7 @@ "@nx/eslint-plugin": "~19.5.0", "@nx/jest": "~19.5.0", "@nx/js": "~19.5.0", + "@o3r/build-helpers": "workspace:^", "@o3r/eslint-plugin": "workspace:^", "@stylistic/eslint-plugin-ts": "~2.4.0", "@types/jest": "~29.5.2", diff --git a/packages/@o3r/test-helpers/package.json b/packages/@o3r/test-helpers/package.json index be571e733f..5249024649 100644 --- a/packages/@o3r/test-helpers/package.json +++ b/packages/@o3r/test-helpers/package.json @@ -46,6 +46,7 @@ "@jest/environment": "~29.7.0", "@jest/types": "~29.6.3", "@nx/eslint-plugin": "~19.5.0", + "@o3r/build-helpers": "workspace:^", "@o3r/eslint-plugin": "workspace:^", "@o3r/schematics": "workspace:^", "@schematics/angular": "~18.2.0", diff --git a/tools/@o3r/workspace-helpers/.eslintrc.cjs b/tools/@o3r/workspace-helpers/.eslintrc.cjs new file mode 100644 index 0000000000..abfad56e5f --- /dev/null +++ b/tools/@o3r/workspace-helpers/.eslintrc.cjs @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +/* eslint-disable quote-props */ + +module.exports = { + 'root': true, + 'parserOptions': { + 'tsconfigRootDir': __dirname, + 'sourceType': 'module', + 'project': [ + 'tsconfig.eslint.json' + ], + }, + 'extends': [ + '../../../.eslintrc.js' + ] +}; diff --git a/tools/@o3r/workspace-helpers/.gitignore b/tools/@o3r/workspace-helpers/.gitignore new file mode 100644 index 0000000000..dfefc61dd2 --- /dev/null +++ b/tools/@o3r/workspace-helpers/.gitignore @@ -0,0 +1,2 @@ +/index.* +.tsbuildinfo diff --git a/tools/@o3r/workspace-helpers/package.json b/tools/@o3r/workspace-helpers/package.json new file mode 100644 index 0000000000..783cb0aa09 --- /dev/null +++ b/tools/@o3r/workspace-helpers/package.json @@ -0,0 +1,55 @@ +{ + "name": "@o3r/workspace-helpers", + "version": "0.0.0-placeholder", + "description": "Build helpers", + "type": "module", + "private": true, + "keywords": [ + "otter", + "amadeus", + "typescript" + ], + "files": [ + "scripts/*.mjs" + ], + "bin": { + "create-monorepo-scope": "./scripts/create-monorepo-scope.mjs", + "doc-links": "./scripts/doc-links.mjs", + "pr-labels": "./scripts/pr-labels.mjs", + "prepare-doc-root-menu-template": "./scripts/prepare-doc-root-menu-template.mjs", + "report-deprecates": "./scripts/report-deprecates.mjs", + "update-doc-summary": "./scripts/update-doc-summary.mjs" + }, + "dependencies": { + "globby": "^14.0.0", + "minimist": "^1.2.6" + }, + "peerDependencies": { + "typescript": "~5.5.4" + }, + "devDependencies": { + "@angular-eslint/eslint-plugin": "~18.3.0", + "@nx/eslint-plugin": "~19.5.0", + "@o3r/eslint-plugin": "workspace:^", + "@stylistic/eslint-plugin-ts": "~2.4.0", + "@types/node": "^20.0.0", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", + "@typescript-eslint/utils": "^7.14.1", + "eslint": "^8.57.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-plugin-jest": "~28.8.0", + "eslint-plugin-jsdoc": "~48.11.0", + "eslint-plugin-prefer-arrow": "~1.2.3", + "eslint-plugin-unicorn": "^54.0.0", + "nx": "~19.5.0", + "typescript": "~5.5.4" + }, + "otter": { + "versionHarmonize": { + "ignore": [ + "globby" + ] + } + } +} diff --git a/tools/@o3r/workspace-helpers/project.json b/tools/@o3r/workspace-helpers/project.json new file mode 100644 index 0000000000..5fa0a1ac3f --- /dev/null +++ b/tools/@o3r/workspace-helpers/project.json @@ -0,0 +1,7 @@ +{ + "name": "workspace-helpers", + "$schema": "https://raw.githubusercontent.com/nrwl/nx/master/packages/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "packages/@o3r/workspace-helpers/scripts", + "tags": [] +} diff --git a/tools/@o3r/build-helpers/schemas/pr-labels.configuration.schema.json b/tools/@o3r/workspace-helpers/schemas/pr-labels.configuration.schema.json similarity index 100% rename from tools/@o3r/build-helpers/schemas/pr-labels.configuration.schema.json rename to tools/@o3r/workspace-helpers/schemas/pr-labels.configuration.schema.json diff --git a/tools/@o3r/build-helpers/scripts/create-monorepo-scope.mjs b/tools/@o3r/workspace-helpers/scripts/create-monorepo-scope.mjs similarity index 100% rename from tools/@o3r/build-helpers/scripts/create-monorepo-scope.mjs rename to tools/@o3r/workspace-helpers/scripts/create-monorepo-scope.mjs diff --git a/tools/@o3r/build-helpers/scripts/doc-links.mjs b/tools/@o3r/workspace-helpers/scripts/doc-links.mjs similarity index 100% rename from tools/@o3r/build-helpers/scripts/doc-links.mjs rename to tools/@o3r/workspace-helpers/scripts/doc-links.mjs diff --git a/tools/@o3r/build-helpers/scripts/pr-labels.mjs b/tools/@o3r/workspace-helpers/scripts/pr-labels.mjs similarity index 100% rename from tools/@o3r/build-helpers/scripts/pr-labels.mjs rename to tools/@o3r/workspace-helpers/scripts/pr-labels.mjs diff --git a/tools/@o3r/build-helpers/scripts/prepare-doc-root-menu-template.mjs b/tools/@o3r/workspace-helpers/scripts/prepare-doc-root-menu-template.mjs similarity index 100% rename from tools/@o3r/build-helpers/scripts/prepare-doc-root-menu-template.mjs rename to tools/@o3r/workspace-helpers/scripts/prepare-doc-root-menu-template.mjs diff --git a/tools/@o3r/workspace-helpers/scripts/report-deprecates.mjs b/tools/@o3r/workspace-helpers/scripts/report-deprecates.mjs new file mode 100644 index 0000000000..038055280f --- /dev/null +++ b/tools/@o3r/workspace-helpers/scripts/report-deprecates.mjs @@ -0,0 +1,146 @@ +/* + * The purpose of this script is to generate a report of the deprecated items in the repository code + * @param root Root folder from which executing the script + * @param output Path the outputted JSON file + * @param ignore List of ignore file patterns (comma separated) + * @param versionPattern RegExp to find the version in the deprecated message + */ + +import { readFile, writeFile } from 'node:fs/promises'; +import { globby as glob } from 'globby'; +import { resolve, relative } from 'node:path'; +import minimist from 'minimist'; +import { createSourceFile, ScriptTarget, isJSDoc, isIdentifier } from 'typescript'; + +const argv = minimist(process.argv.slice(2)); +const root = argv.root ? resolve(process.cwd(), argv.root) : process.cwd(); +const output = resolve(process.cwd(), argv.output || argv.o || 'deprecate-report.json'); +const ignore = argv.ignore ? argv.ignore.split(',') : [ + '**/dist/**' +]; +const versionPattern = argv.versionPattern ? new RegExp(argv.versionPattern) : /[vV]([0-9]+(?:[.][0-9]+)?)/; + +/** + * Work through the TS Node to find the deprecated node + * @param {import("typescript").SourceFile} sourceFile + * @param {import("typescript").Node} node + * @returns {import("typescript").Node} + */ +const workThroughTypescript = (sourceFile, node, index) => { + const children = node.getChildren(sourceFile) + .filter((n) => !isJSDoc(n) && n.getStart(sourceFile, true) <= index && n.getEnd() > index) + + return children.reduce((acc, n) => workThroughTypescript(sourceFile, n, index) || acc, node) +}; + +/** Get deprecated items in TS files */ +const getTypescriptDeprecations = async () => { + const deprecateRegExp = /@deprecated/g; + const deprecateLineRegExp = /@deprecated(.*)$/mg; + const files = await glob('**/*.{c,m,}ts', {cwd: root, ignore}); + const reports = []; + for (let file of files) { + file = resolve(root, file); + const content = await readFile(file, { encoding: 'utf8' }); + const match = [...content.matchAll(deprecateRegExp)]; + if (!match.length) { + continue; + } + + const sourceFile = createSourceFile( + file, + content, + ScriptTarget.ES2022, + true + ); + reports.push(...match + .map(({ index }) => workThroughTypescript(sourceFile, sourceFile, index)) + .map((node) => { + const deprecationInfo = [ + ...node + .getFullText(sourceFile) + .matchAll(deprecateLineRegExp) + ].map(([, description]) => description.trim()).join('\n') + return { + file, + report: { + node: typeof node.name !== 'undefined' && isIdentifier(node.name) + ? node.name.getText() + : node.getText(sourceFile), + deprecationInfo, + version: deprecationInfo.match(versionPattern)?.[1] + } + }; + }) + ); + } + return reports; +}; + +/** + * Work through the JSON fields do find the deprecated item + * @param {Object} json + * @param {RegExp} nodeNameToDetect + */ +const workThroughJson = (json, nodeNameToDetect) => { + const memory = []; + const rec = (node, ancestors, mem) => { + if (Array.isArray(node)) { + return node.reduce((acc, n) => rec(n, ancestors, acc), mem); + } else if (node === null || typeof node !== 'object') { + return mem; + } + + const entries = Object.entries(node); + entries + .map(([key]) => key) + .filter((key) => nodeNameToDetect.test(key)) + .forEach((key) => { + const deprecationInfo = node[key]; + mem.push({ + node: ancestors, + deprecationInfo, + version: deprecationInfo.match(versionPattern)?.[1] + }); + }); + + return entries.reduce((acc, [key, value]) => rec(value, `${ancestors}.${key}`, acc), mem); + } + + return rec(json, '', memory); +} + +/** Get deprecated items in JSON files */ +const getJsonDeprecations = async () => { + const deprecateNodeRegExp = /"[$]?deprecated"/; + const files = await glob('**/*.json', { cwd: root, ignore }); + const contents = await Promise.all(files + .map((file) => resolve(root, file)) + .map(async (file) => ({ file, content: await readFile(file, { encoding: 'utf8' }) })) + ); + + return contents + .filter(({ content }) => !!content.match(deprecateNodeRegExp)) + .map(({ file, content }) => ({ + file, + content: JSON.parse(content) + })) + .map(({ file, content }) => ({ + file, + report: workThroughJson(content, deprecateNodeRegExp) + })) + .filter(({report}) => !!report.length); +}; + +// Executed at CLI call +void (async() => { + const displayReport = (await Promise.all([ + getJsonDeprecations(), + getTypescriptDeprecations() + ])).flat().reduce((acc, {file, report}) => { + (acc[relative(root, file)] ||= []).push(report); + return acc; + },{}); + + writeFile(output, JSON.stringify(displayReport, null, 2)); +})(); diff --git a/tools/@o3r/build-helpers/scripts/update-doc-summary.mjs b/tools/@o3r/workspace-helpers/scripts/update-doc-summary.mjs similarity index 100% rename from tools/@o3r/build-helpers/scripts/update-doc-summary.mjs rename to tools/@o3r/workspace-helpers/scripts/update-doc-summary.mjs diff --git a/tools/@o3r/workspace-helpers/tsconfig.eslint.json b/tools/@o3r/workspace-helpers/tsconfig.eslint.json new file mode 100644 index 0000000000..7abdbdaeb4 --- /dev/null +++ b/tools/@o3r/workspace-helpers/tsconfig.eslint.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../tsconfig.build.json", + "compilerOptions": { + "allowJs": true + }, + "include": [ + "./scripts/*.mjs" + ] +} diff --git a/yarn.lock b/yarn.lock index d240ade63a..401aec4319 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8610,11 +8610,11 @@ __metadata: "@nx/jest": "npm:~19.5.0" "@nx/js": "npm:~19.5.0" "@nx/workspace": "npm:~19.5.0" - "@o3r/build-helpers": "workspace:^" "@o3r/eslint-config-otter": "workspace:^" "@o3r/eslint-plugin": "workspace:^" "@o3r/telemetry": "workspace:^" "@o3r/workspace": "workspace:^" + "@o3r/workspace-helpers": "workspace:^" "@playwright/test": "npm:~1.47.0" "@popperjs/core": "npm:^2.11.5" "@schematics/angular": "npm:~18.2.0" @@ -9457,6 +9457,7 @@ __metadata: "@nx/eslint-plugin": "npm:~19.5.0" "@nx/jest": "npm:~19.5.0" "@o3r/application": "workspace:^" + "@o3r/build-helpers": "workspace:^" "@o3r/components": "workspace:^" "@o3r/configuration": "workspace:^" "@o3r/core": "workspace:^" @@ -9902,6 +9903,7 @@ __metadata: "@nx/eslint-plugin": "npm:~19.5.0" "@nx/jest": "npm:~19.5.0" "@nx/js": "npm:~19.5.0" + "@o3r/build-helpers": "workspace:^" "@o3r/eslint-plugin": "workspace:^" "@stylistic/eslint-plugin-ts": "npm:~2.4.0" "@types/jest": "npm:~29.5.2" @@ -9953,6 +9955,7 @@ __metadata: "@jest/environment": "npm:~29.7.0" "@jest/types": "npm:~29.6.3" "@nx/eslint-plugin": "npm:~19.5.0" + "@o3r/build-helpers": "workspace:^" "@o3r/eslint-plugin": "workspace:^" "@o3r/schematics": "workspace:^" "@schematics/angular": "npm:~18.2.0" @@ -10289,6 +10292,40 @@ __metadata: languageName: unknown linkType: soft +"@o3r/workspace-helpers@workspace:^, @o3r/workspace-helpers@workspace:tools/@o3r/workspace-helpers": + version: 0.0.0-use.local + resolution: "@o3r/workspace-helpers@workspace:tools/@o3r/workspace-helpers" + dependencies: + "@angular-eslint/eslint-plugin": "npm:~18.3.0" + "@nx/eslint-plugin": "npm:~19.5.0" + "@o3r/eslint-plugin": "workspace:^" + "@stylistic/eslint-plugin-ts": "npm:~2.4.0" + "@types/node": "npm:^20.0.0" + "@typescript-eslint/eslint-plugin": "npm:^7.14.1" + "@typescript-eslint/parser": "npm:^7.14.1" + "@typescript-eslint/utils": "npm:^7.14.1" + eslint: "npm:^8.57.0" + eslint-import-resolver-node: "npm:^0.3.9" + eslint-plugin-jest: "npm:~28.8.0" + eslint-plugin-jsdoc: "npm:~48.11.0" + eslint-plugin-prefer-arrow: "npm:~1.2.3" + eslint-plugin-unicorn: "npm:^54.0.0" + globby: "npm:^14.0.0" + minimist: "npm:^1.2.6" + nx: "npm:~19.5.0" + typescript: "npm:~5.5.4" + peerDependencies: + typescript: ~5.5.4 + bin: + create-monorepo-scope: ./scripts/create-monorepo-scope.mjs + doc-links: ./scripts/doc-links.mjs + pr-labels: ./scripts/pr-labels.mjs + prepare-doc-root-menu-template: ./scripts/prepare-doc-root-menu-template.mjs + report-deprecates: ./scripts/report-deprecates.mjs + update-doc-summary: ./scripts/update-doc-summary.mjs + languageName: unknown + linkType: soft + "@o3r/workspace@workspace:^, @o3r/workspace@workspace:packages/@o3r/workspace": version: 0.0.0-use.local resolution: "@o3r/workspace@workspace:packages/@o3r/workspace"