diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6de687ba1da4..c257909a580d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -149,7 +149,7 @@ jobs: - name: Verify tooling setup run: pnpm check-tooling-setup - name: Build and create package artifacts - run: ./scripts/create-package-archives.js --suffix "pr${{github.event.number}}-$(git rev-parse --short HEAD)" + run: ./scripts/create-package-archives.mjs --suffix "pr${{github.event.number}}-$(git rev-parse --short HEAD)" - name: Upload artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: diff --git a/integration/linker/link-packages-test.mjs b/integration/linker/link-packages-test.mjs index f9bb64a4f060..8213738af617 100644 --- a/integration/linker/link-packages-test.mjs +++ b/integration/linker/link-packages-test.mjs @@ -8,7 +8,7 @@ import {NodeJSFileSystem, ConsoleLogger, LogLevel} from '@angular/compiler-cli'; import fs from 'fs'; import path from 'path'; import babel from '@babel/core'; -import glob from 'glob'; +import {sync as globSync} from 'glob'; import chalk from 'chalk'; /** File system used by the Angular linker plugin. */ @@ -62,7 +62,7 @@ if (failedPackages) { * @returns An object containing linker failures and passed files. */ function testPackage(pkg) { - const entryPointFesmFiles = glob.sync(`fesm2022/**/*.mjs`, {cwd: pkg.pkgPath}); + const entryPointFesmFiles = globSync(`fesm2022/**/*.mjs`, {cwd: pkg.pkgPath}); const passedFiles = []; const failures = []; diff --git a/package.json b/package.json index 464b4722c454..0015e61a0d5f 100644 --- a/package.json +++ b/package.json @@ -29,13 +29,13 @@ "lint": "pnpm -s tslint && pnpm -s stylelint && pnpm -s ownerslint && pnpm -s ng-dev format changed --check", "e2e": "bazel test //src/... --build_tag_filters=e2e --test_tag_filters=e2e --build_tests_only", "deploy-dev-app": "node ./scripts/deploy-dev-app.js", - "breaking-changes": "ts-node --project scripts/tsconfig.json scripts/breaking-changes.ts", - "check-entry-point-setup": "node ./scripts/check-entry-point-setup.js", - "check-package-externals": "ts-node --project scripts/tsconfig.json scripts/check-package-externals.ts", + "breaking-changes": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only scripts/breaking-changes.mts", + "check-entry-point-setup": "node ./scripts/check-entry-point-setup.mjs", + "check-package-externals": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only scripts/check-package-externals.mts", "format": "pnpm -s ng-dev format changed", "cherry-pick-patch": "ts-node --project tools/cherry-pick-patch/tsconfig.json tools/cherry-pick-patch/cherry-pick-patch.ts", - "ownerslint": "ts-node --project scripts/tsconfig.json scripts/ownerslint.ts", - "detect-component-id-collisions": "ts-node --project scripts/tsconfig.json scripts/detect-component-id-collisions.ts", + "ownerslint": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only scripts/ownerslint.mts", + "detect-component-id-collisions": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only scripts/detect-component-id-collisions.mts", "tslint": "tslint -c tslint.json --project ./tsconfig.json", "stylelint": "stylelint \"(src|docs)/**/*.+(css|scss)\" --config .stylelintrc.json", "resync-caretaker-app": "ts-node --project scripts/tsconfig.json scripts/caretaking/resync-caretaker-app-prs.ts", @@ -95,7 +95,6 @@ "@types/glob": "^8.0.0", "@types/jasmine": "^5.0.0", "@types/luxon": "^3.0.0", - "@types/marked": "^2.0.0", "@types/minimatch": "^5.1.2", "@types/node": "^22.14.1", "@types/selenium-webdriver": "^3.0.17", @@ -104,13 +103,13 @@ "@types/yargs": "^17.0.8", "autoprefixer": "^10.4.2", "axe-core": "^4.10.3", - "chalk": "^4.1.0", + "chalk": "^5.4.1", "dgeni": "^0.4.14", "dgeni-packages": "^0.30.0", "esbuild": "^0.25.0", "firebase-tools": "14.4.0", "fs-extra": "^11.0.0", - "glob": "^7.2.0", + "glob": "^11.0.3", "highlight.js": "^11.0.0", "husky": "^9.0.1", "jasmine": "^5.6.0", @@ -124,7 +123,7 @@ "karma-parallel": "^0.3.1", "karma-sourcemap-loader": "^0.4.0", "magic-string": "0.30.17", - "marked": "^2.0.0", + "marked": "^15.0.12", "minimatch": "^3.0.4", "node-fetch": "^2.6.0", "parse5": "^7.1.2", @@ -141,6 +140,7 @@ "semver": "^7.3.5", "send": "^0.19.0", "shelljs": "^0.10.0", + "slugify": "^1.6.6", "source-map-support": "^0.5.21", "stylelint": "^14.14.0", "terser": "^5.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23e271a6424d..72a0ac739e33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -188,9 +188,6 @@ importers: '@types/luxon': specifier: ^3.0.0 version: 3.6.2 - '@types/marked': - specifier: ^2.0.0 - version: 2.0.5 '@types/minimatch': specifier: ^5.1.2 version: 5.1.2 @@ -216,8 +213,8 @@ importers: specifier: ^4.10.3 version: 4.10.3 chalk: - specifier: ^4.1.0 - version: 4.1.2 + specifier: ^5.4.1 + version: 5.4.1 dgeni: specifier: ^0.4.14 version: 0.4.14 @@ -234,8 +231,8 @@ importers: specifier: ^11.0.0 version: 11.3.0 glob: - specifier: ^7.2.0 - version: 7.2.3 + specifier: ^11.0.3 + version: 11.0.3 highlight.js: specifier: ^11.0.0 version: 11.11.1 @@ -276,8 +273,8 @@ importers: specifier: 0.30.17 version: 0.30.17 marked: - specifier: ^2.0.0 - version: 2.1.3 + specifier: ^15.0.12 + version: 15.0.12 minimatch: specifier: ^3.0.4 version: 3.1.2 @@ -326,6 +323,9 @@ importers: shelljs: specifier: ^0.10.0 version: 0.10.0 + slugify: + specifier: ^1.6.6 + version: 1.6.6 source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -2088,6 +2088,14 @@ packages: '@types/node': optional: true + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2982,9 +2990,6 @@ packages: '@types/luxon@3.6.2': resolution: {integrity: sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==} - '@types/marked@2.0.5': - resolution: {integrity: sha512-shRZ7XnYFD/8n8zSjKvFdto1QNSf4tONZIlNEZGrJe8GsOE8DL/hG1Hbl8gZlfLnjS7+f5tZGIaTgfpyW38h4w==} - '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -4926,6 +4931,11 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true + glob@11.0.3: + resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + engines: {node: 20 || >=22} + hasBin: true + glob@5.0.15: resolution: {integrity: sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==} deprecated: Glob versions prior to v9 are no longer supported @@ -5701,6 +5711,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + jasmine-core@2.8.0: resolution: {integrity: sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==} @@ -6119,6 +6133,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.1.0: + resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -6193,9 +6211,9 @@ packages: engines: {node: '>= 18'} hasBin: true - marked@2.1.3: - resolution: {integrity: sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==} - engines: {node: '>= 10'} + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} hasBin: true marky@1.3.0: @@ -6300,6 +6318,10 @@ packages: minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + minimatch@3.0.8: resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} @@ -6850,6 +6872,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + path-to-regexp@0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} @@ -7727,6 +7753,10 @@ packages: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} + slugify@1.6.6: + resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} + engines: {node: '>=8.0.0'} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -10485,6 +10515,12 @@ snapshots: optionalDependencies: '@types/node': 22.14.1 + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -11381,8 +11417,6 @@ snapshots: '@types/luxon@3.6.2': {} - '@types/marked@2.0.5': {} - '@types/mime@1.3.5': {} '@types/minimatch@5.1.2': {} @@ -13718,6 +13752,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@11.0.3: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + glob@5.0.15: dependencies: inflight: 1.0.6 @@ -14569,6 +14612,10 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + jasmine-core@2.8.0: {} jasmine-core@3.99.1: {} @@ -15107,6 +15154,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.1.0: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -15201,7 +15250,7 @@ snapshots: marked@13.0.3: {} - marked@2.1.3: {} + marked@15.0.12: {} marky@1.3.0: {} @@ -15284,6 +15333,10 @@ snapshots: minimalistic-assert@1.0.1: {} + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.0.8: dependencies: brace-expansion: 1.1.11 @@ -15907,6 +15960,11 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-scurry@2.0.0: + dependencies: + lru-cache: 11.1.0 + minipass: 7.1.2 + path-to-regexp@0.1.12: {} path-to-regexp@1.9.0: @@ -16963,6 +17021,8 @@ snapshots: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 + slugify@1.6.6: {} + smart-buffer@4.2.0: {} snake-case@2.1.0: diff --git a/scripts/breaking-changes.ts b/scripts/breaking-changes.mts similarity index 96% rename from scripts/breaking-changes.ts rename to scripts/breaking-changes.mts index 7a749db84529..049d3d9f0948 100644 --- a/scripts/breaking-changes.ts +++ b/scripts/breaking-changes.mts @@ -5,8 +5,10 @@ import ts from 'typescript'; const projectRoot = process.cwd(); +const packageJson = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8')); + // Current version from the package.json. Splits it on the dash to ignore pre-release labels. -const packageVersion = require(join(projectRoot, 'package.json')).version.split('-')[0]; +const packageVersion = packageJson.version.split('-')[0]; // Regex used to extract versions from a string. const versionRegex = /\d+\.\d+\.\d+/; diff --git a/scripts/check-entry-point-setup.js b/scripts/check-entry-point-setup.mjs similarity index 84% rename from scripts/check-entry-point-setup.js rename to scripts/check-entry-point-setup.mjs index f2826395593c..6606360d5066 100644 --- a/scripts/check-entry-point-setup.js +++ b/scripts/check-entry-point-setup.mjs @@ -7,15 +7,15 @@ * script through a manifest file (generated by Bazel) */ -const {join, dirname} = require('path'); -const {sync: globSync} = require('glob'); -const minimatch = require('minimatch'); -const fs = require('fs'); -const chalk = require('chalk'); +import {join, dirname} from 'path'; +import {sync as globSync} from 'glob'; +import {readFileSync} from 'fs'; +import minimatch from 'minimatch'; +import chalk from 'chalk'; const [entryPointManifest] = process.argv.slice(2); -const entryPoints = JSON.parse(fs.readFileSync(entryPointManifest, 'utf8')); -const packagesDir = join(__dirname, '../src'); +const entryPoints = JSON.parse(readFileSync(entryPointManifest, 'utf8')); +const packagesDir = join(process.cwd(), 'src'); /** * Globs that matches directories which should never be considered diff --git a/scripts/check-package-externals.ts b/scripts/check-package-externals.mts similarity index 98% rename from scripts/check-package-externals.ts rename to scripts/check-package-externals.mts index 27b335b0a0b3..9f6fbce9f7bc 100644 --- a/scripts/check-package-externals.ts +++ b/scripts/check-package-externals.mts @@ -14,7 +14,7 @@ import minimatch from 'minimatch'; import {join, relative} from 'path'; import ts from 'typescript'; -const projectRoot = join(__dirname, '../'); +const projectRoot = process.cwd(); const args = process.argv.slice(2); if (args.length !== 1) { diff --git a/scripts/create-legacy-tests-bundle.mjs b/scripts/create-legacy-tests-bundle.mjs index 5f1f62c9ce2a..9754f6d82567 100644 --- a/scripts/create-legacy-tests-bundle.mjs +++ b/scripts/create-legacy-tests-bundle.mjs @@ -4,9 +4,8 @@ import babel from '@babel/core'; import child_process from 'child_process'; import esbuild from 'esbuild'; import fs from 'fs'; -import glob from 'glob'; +import {sync as globSync} from 'glob'; import module from 'module'; -import path from 'path'; import {dirname, join, relative} from 'path'; import * as sass from 'sass'; import url from 'url'; @@ -51,8 +50,8 @@ async function main() { const specEntryPointFile = await createEntryPointSpecFile(); // Copy tsconfig so that ESBuild can leverage its path mappings. - const esbuildTsconfig = path.join(legacyOutputDir, 'tsconfig-esbuild.json'); - await fs.promises.cp(path.join(packagesDir, 'bazel-tsconfig-build.json'), esbuildTsconfig); + const esbuildTsconfig = join(legacyOutputDir, 'tsconfig-esbuild.json'); + await fs.promises.cp(join(packagesDir, 'bazel-tsconfig-build.json'), esbuildTsconfig); const result = await esbuild.build({ bundle: true, @@ -83,7 +82,7 @@ async function main() { * explicitly added. */ async function compileSassFiles() { - const sassFiles = glob.sync('src/**/!(_*|theme).scss', {cwd: projectDir, absolute: true}); + const sassFiles = globSync('src/**/!(_*|theme).scss', {cwd: projectDir, absolute: true}); const writeTasks = []; let count = 0; @@ -132,7 +131,7 @@ async function compileProjectWithNgtsc() { * to bundle all specs in an IIFE file. */ async function createEntryPointSpecFile() { - const testFiles = glob.sync('**/*.spec.js', {absolute: true, cwd: legacyOutputDir}); + const testFiles = globSync('**/*.spec.js', {absolute: true, cwd: legacyOutputDir}); let specEntryPointFile = `import './test/angular-test.init.ts';`; let i = 0; diff --git a/scripts/create-package-archives.js b/scripts/create-package-archives.mjs similarity index 66% rename from scripts/create-package-archives.js rename to scripts/create-package-archives.mjs index b22904d75d4e..537e0e782a05 100755 --- a/scripts/create-package-archives.js +++ b/scripts/create-package-archives.mjs @@ -9,12 +9,11 @@ * the primary `cdk` and `material` packages, and also don't run for pull requests. */ -const {join} = require('path'); -const {rm, mkdir, test, ls, set, exec, cd} = require('shelljs'); -const {red, green} = require('chalk'); -const yargs = require('yargs'); +import {join} from 'path'; +import sh from 'shelljs'; +import chalk from 'chalk'; +import yargs from 'yargs'; -const projectDir = join(__dirname, '../'); const archivesDir = 'dist/release-archives'; const releasesDir = 'dist/releases'; const {suffix} = yargs(process.argv.slice(2)) @@ -23,34 +22,35 @@ const {suffix} = yargs(process.argv.slice(2)) .parseSync(); // Fail if any ShellJS command fails. -set('-e'); +sh.set('-e'); -cd(projectDir); - -if (!test('-e', releasesDir)) { - console.error(red('The release output has not been built.')); +if (!sh.test('-e', releasesDir)) { + console.error(chalk.red('The release output has not been built.')); process.exit(1); } -rm('-Rf', archivesDir); -mkdir('-p', archivesDir); +sh.rm('-Rf', archivesDir); +sh.mkdir('-p', archivesDir); -const builtPackages = ls(releasesDir) +const builtPackages = sh + .ls(releasesDir) .map(name => ({name, path: join(releasesDir, name)})) - .filter(pkg => test('-d', pkg.path)); + .filter(pkg => sh.test('-d', pkg.path)); // If multiple packages should be archived, we also generate a single archive that // contains all packages. This makes it easier to transfer the release packages. if (builtPackages.length > 1) { console.info('Creating archive with all packages..'); - exec(`tar --create --gzip --directory ${releasesDir} --file ${archivesDir}/all-${suffix}.tgz .`); + sh.exec( + `tar --create --gzip --directory ${releasesDir} --file ${archivesDir}/all-${suffix}.tgz .`, + ); } for (const pkg of builtPackages) { console.info(`Creating archive for package: ${pkg.name}`); - exec( + sh.exec( `tar --create --gzip --directory ${pkg.path} --file ${archivesDir}/${pkg.name}-${suffix}.tgz .`, ); } -console.info(green(`Created package archives in: ${archivesDir}`)); +console.info(chalk.green(`Created package archives in: ${archivesDir}`)); diff --git a/scripts/detect-component-id-collisions.ts b/scripts/detect-component-id-collisions.mts similarity index 95% rename from scripts/detect-component-id-collisions.ts rename to scripts/detect-component-id-collisions.mts index 8202bd7b9799..913b0dded37d 100644 --- a/scripts/detect-component-id-collisions.ts +++ b/scripts/detect-component-id-collisions.mts @@ -9,7 +9,7 @@ import {sync as glob} from 'glob'; const errors: string[] = []; const seenMetadata = new Map(); -const fileToCheck = join(__dirname, '../src/**/!(*.spec).ts'); +const fileToCheck = join(process.cwd(), 'src/**/!(*.spec).ts'); const ignoredPatterns = [ '**/components-examples/**', '**/dev-app/**', @@ -141,13 +141,16 @@ function serializeValue(node: ts.Node): string { .slice() // Sort the fields since JS engines preserve the order properties in object literals. .sort((a, b) => (a.name?.getText() || '').localeCompare(b.name?.getText() || '')) - .reduce((accumulator, prop) => { - if (ts.isPropertyAssignment(prop)) { - accumulator[prop.name.getText()] = serializeValue(prop.initializer); - } - - return accumulator; - }, {} as Record); + .reduce( + (accumulator, prop) => { + if (ts.isPropertyAssignment(prop)) { + accumulator[prop.name.getText()] = serializeValue(prop.initializer); + } + + return accumulator; + }, + {} as Record, + ); return JSON.stringify(serialized); } diff --git a/scripts/ownerslint.ts b/scripts/ownerslint.mts similarity index 90% rename from scripts/ownerslint.ts rename to scripts/ownerslint.mts index e1f39fe9b839..e540b0c79831 100644 --- a/scripts/ownerslint.ts +++ b/scripts/ownerslint.mts @@ -1,6 +1,6 @@ import chalk from 'chalk'; import {readdirSync, readFileSync, statSync} from 'fs'; -import {IMinimatch, Minimatch} from 'minimatch'; +import minimatch from 'minimatch'; import {join} from 'path'; /** @@ -23,7 +23,7 @@ const ownedPaths = readFileSync(ownersFilePath, 'utf8') // Split off just the path glob. .map(line => line.split(/\s+/)[0]) // Turn paths into Minimatch objects. - .map(path => new Minimatch(path, {dot: true, matchBase: true})); + .map(path => new minimatch.Minimatch(path, {dot: true, matchBase: true})); const ignoredPaths = readFileSync(gitIgnorePath, 'utf8') .split('\n') @@ -32,7 +32,7 @@ const ignoredPaths = readFileSync(gitIgnorePath, 'utf8') // Remove empty lines and comments. .filter(line => line && !line.startsWith('#')) // Turn paths into Minimatch objects. - .map(path => new Minimatch(path, {dot: true, matchBase: true})); + .map(path => new minimatch.Minimatch(path, {dot: true, matchBase: true})); for (let paths = getChildPaths('.'); paths.length; ) { paths = Array.prototype.concat( @@ -72,7 +72,7 @@ if (errors) { } /** Check if the given path is owned by the given owned path matcher. */ -function isOwnedBy(path: string, ownedPath: IMinimatch) { +function isOwnedBy(path: string, ownedPath: minimatch.IMinimatch) { // If the owned path ends with `**` its safe to eliminate whole directories. if (ownedPath.pattern.endsWith('**') || statSync(path).isFile()) { return ownedPath.match('/' + path); diff --git a/tools/highlight-files/highlight-code-block.ts b/tools/highlight-files/highlight-code-block.ts index f0383dff6c71..ce7a38f15537 100644 --- a/tools/highlight-files/highlight-code-block.ts +++ b/tools/highlight-files/highlight-code-block.ts @@ -4,7 +4,7 @@ import highlightJs from 'highlight.js'; * Transforms a given code block into its corresponding HTML output. We do this using * highlight.js because it allows us to show colored code blocks in our documentation. */ -export function highlightCodeBlock(code: string, language: string) { +export function highlightCodeBlock(code: string, language?: string) { if (language) { return highlightJs.highlight(code, { language: language.toLowerCase() === 'ts' ? 'typescript' : language, diff --git a/tools/markdown-to-html/BUILD.bazel b/tools/markdown-to-html/BUILD.bazel index 0a0a08a74051..361934dde196 100644 --- a/tools/markdown-to-html/BUILD.bazel +++ b/tools/markdown-to-html/BUILD.bazel @@ -13,8 +13,8 @@ ts_project( ), tsconfig = "//tools:tsconfig", deps = [ - "//:node_modules/@types/marked", "//:node_modules/marked", + "//:node_modules/slugify", "//tools/highlight-files:sources", ], ) diff --git a/tools/markdown-to-html/docs-marked-renderer.ts b/tools/markdown-to-html/docs-marked-renderer.ts index 8893af3e4784..0686db40b181 100644 --- a/tools/markdown-to-html/docs-marked-renderer.ts +++ b/tools/markdown-to-html/docs-marked-renderer.ts @@ -1,5 +1,7 @@ -import {Renderer, Slugger} from 'marked'; +import {Renderer, Tokens} from 'marked'; import {basename, extname} from 'path'; +import slugify from 'slugify'; +import {highlightCodeBlock} from '../highlight-files/highlight-code-block'; /** Regular expression that matches example comments. */ const exampleCommentRegex = //g; @@ -18,38 +20,45 @@ export class DocsMarkdownRenderer extends Renderer { /** Set of fragment links discovered in the currently rendered file. */ private _referencedFragments = new Set(); - /** - * Slugger provided by the `marked` package. Can be used to create unique - * ids for headings. - */ - private _slugger = new Slugger(); + /** IDs that have been generated during Markdown parsing. */ + private _seenIds = new Set(); /** * Transforms a markdown heading into the corresponding HTML output. In our case, we * want to create a header-link for each H2, H3, and H4 heading. This allows users to jump to * specific parts of the docs. */ - heading(label: string, level: number, raw: string) { - if (level === 2 || level === 3 || level === 4 || level === 5 || level === 6) { - const headingId = this._slugger.slug(raw); + heading(tag: Tokens.Heading) { + const depth = tag.depth; + const content = this.parser.parseInline(tag.tokens); + + if (depth === 2 || depth === 3 || depth === 4 || depth === 5 || depth === 6) { + const headingId = slugify(tag.text, {lower: true, strict: true}); + + this._seenIds.add(headingId); return ` - + - ${label} - + ${content} + `; } - return `${label}`; + return `${content}`; } /** Transforms markdown links into the corresponding HTML output. */ - link(href: string, title: string, text: string) { + link(link: Tokens.Link) { + const {href} = link; + // We only want to fix up markdown links that are relative and do not refer to guides already. // Otherwise we always map the link to the "guide/" path. // TODO(devversion): remove this logic and just disallow relative paths. if (!href.startsWith('http') && !href.startsWith('#') && !href.includes('guide/')) { - return super.link(`guide/${basename(href, extname(href))}`, title, text); + return super.link({ + ...link, + href: `guide/${basename(href, extname(href))}`, + }); } // Keep track of all fragments discovered in a file. @@ -57,7 +66,7 @@ export class DocsMarkdownRenderer extends Renderer { this._referencedFragments.add(href.slice(1)); } - return super.link(href, title, text); + return super.link(link); } /** @@ -82,8 +91,8 @@ export class DocsMarkdownRenderer extends Renderer { * turns into * `
` */ - html(html: string) { - html = html.replace(exampleCommentRegex, (_match: string, content: string) => { + html(content: Tokens.HTML | Tokens.Tag) { + return content.raw.replace(exampleCommentRegex, (_match: string, content: string) => { let replacement: string; // using [\s\S]* because .* does not match line breaks @@ -102,8 +111,11 @@ export class DocsMarkdownRenderer extends Renderer { return `${exampleStartMarker}${replacement}${exampleEndMarker}`; }); + } - return super.html(html); + code(block: Tokens.Code): string { + const langClass = block.lang ? ` class="language-${block.lang}"` : ''; + return `
${highlightCodeBlock(block.text, block.lang)}
`; } /** @@ -116,7 +128,7 @@ export class DocsMarkdownRenderer extends Renderer { // Collect any fragment links that do not resolve to existing fragments in the // rendered file. We want to error for broken fragment links. this._referencedFragments.forEach(id => { - if (this._slugger.seen[id] === undefined) { + if (!this._seenIds.has(id)) { failures.push(`Found link to "${id}". This heading does not exist.`); } }); @@ -127,7 +139,7 @@ export class DocsMarkdownRenderer extends Renderer { process.exit(1); } - this._slugger.seen = {}; + this._seenIds.clear(); this._referencedFragments.clear(); const markdownOpen = '
'; diff --git a/tools/markdown-to-html/transform-markdown.ts b/tools/markdown-to-html/transform-markdown.ts index 705cd7660419..3fd96933e2d2 100644 --- a/tools/markdown-to-html/transform-markdown.ts +++ b/tools/markdown-to-html/transform-markdown.ts @@ -4,16 +4,15 @@ */ import {readFileSync, writeFileSync} from 'fs'; -import marked from 'marked'; +import {marked} from 'marked'; import {join} from 'path'; -import {highlightCodeBlock} from '../highlight-files/highlight-code-block'; import {DocsMarkdownRenderer} from './docs-marked-renderer'; // Custom markdown renderer for transforming markdown files for the docs. const markdownRenderer = new DocsMarkdownRenderer(); // Setup our custom docs renderer by default. -marked.setOptions({renderer: markdownRenderer, highlight: highlightCodeBlock}); +marked.setOptions({renderer: markdownRenderer}); if (require.main === module) { // The script expects the bazel-bin path as first argument. All remaining arguments will be @@ -25,7 +24,7 @@ if (require.main === module) { inputFiles.forEach(inputPath => { const outputPath = join(bazelBinPath, `${inputPath}.html`); const htmlOutput = markdownRenderer.finalizeOutput( - marked(readFileSync(inputPath, 'utf8')), + marked.parse(readFileSync(inputPath, 'utf8'), {async: false}), inputPath, ); diff --git a/tools/release-checks/npm-package-output/check-package.mts b/tools/release-checks/npm-package-output/check-package.mts index d8b8bf0381c6..22d5fb03c413 100644 --- a/tools/release-checks/npm-package-output/check-package.mts +++ b/tools/release-checks/npm-package-output/check-package.mts @@ -1,6 +1,6 @@ import {Log, bold, yellow} from '@angular/ng-dev'; import {existsSync} from 'fs'; -import glob from 'glob'; +import {sync as globSync} from 'glob'; import {basename, dirname, join} from 'path'; import { @@ -47,9 +47,9 @@ export function checkReleasePackage( failures.set(message, filePaths); }; - const jsFiles = glob.sync(releaseJsFilesGlob, {cwd: packagePath, absolute: true}); - const typeDefinitions = glob.sync(releaseTypeDefinitionsGlob, {cwd: packagePath, absolute: true}); - const packageJsonFiles = glob.sync(packageJsonFilesGlob, {cwd: packagePath, absolute: true}); + const jsFiles = globSync(releaseJsFilesGlob, {cwd: packagePath, absolute: true}); + const typeDefinitions = globSync(releaseTypeDefinitionsGlob, {cwd: packagePath, absolute: true}); + const packageJsonFiles = globSync(packageJsonFilesGlob, {cwd: packagePath, absolute: true}); // We want to walk through each bundle within the current package and run // release validations that ensure that the bundles are not invalid. diff --git a/tools/release-checks/npm-package-output/output-validations.mts b/tools/release-checks/npm-package-output/output-validations.mts index 0ebc068a1f86..09f987e3f03c 100644 --- a/tools/release-checks/npm-package-output/output-validations.mts +++ b/tools/release-checks/npm-package-output/output-validations.mts @@ -1,5 +1,5 @@ import {existsSync, readFileSync} from 'fs'; -import glob from 'glob'; +import {sync as globSync} from 'glob'; import {basename, dirname, isAbsolute, join} from 'path'; import semver from 'semver'; @@ -146,7 +146,7 @@ export function checkMaterialPackage(packagePath: string): string[] { const newThemingFilePath = join(packagePath, '_index.scss'); const failures: string[] = []; - if (glob.sync('*.css', {cwd: prebuiltThemesPath}).length === 0) { + if (globSync('*.css', {cwd: prebuiltThemesPath}).length === 0) { failures.push('No prebuilt themes could be found.'); } @@ -161,7 +161,7 @@ export function checkMaterialPackage(packagePath: string): string[] { * Checks whether the prebuilt CDK files are part of the release output. */ export function checkCdkPackage(packagePath: string): string[] { - const prebuiltFiles = glob.sync('*-prebuilt.css', {cwd: packagePath}).map(path => basename(path)); + const prebuiltFiles = globSync('*-prebuilt.css', {cwd: packagePath}).map(path => basename(path)); const newApiFilePath = join(packagePath, '_index.scss'); const failures = ['overlay', 'a11y', 'text-field'] .filter(name => !prebuiltFiles.includes(`${name}-prebuilt.css`))