diff --git a/.ng-dev/google-sync-config.json b/.ng-dev/google-sync-config.json index 4e0adfe916a..1203ba85a15 100644 --- a/.ng-dev/google-sync-config.json +++ b/.ng-dev/google-sync-config.json @@ -1,12 +1,6 @@ { - "syncedFilePatterns": [ - "LICENSE", - "modules/benchmarks/**", - "packages/**" - ], - "separateFilePatterns": [ - "packages/core/primitives/**" - ], + "syncedFilePatterns": ["LICENSE", "modules/benchmarks/**", "packages/**"], + "separateFilePatterns": ["packages/core/primitives/**"], "alwaysExternalFilePatterns": [ "packages/*", "packages/bazel/*", @@ -26,6 +20,7 @@ "packages/compiler-cli/private/tooling.ts", "packages/compiler-cli/private/babel.d.ts", "packages/compiler-cli/src/bin/**", + "packages/core/schematics/utils/tsurge/helpers/angular_devkit/**", "packages/docs/**", "packages/elements/schematics/**", "packages/examples/**", diff --git a/packages/core/schematics/utils/tsurge/helpers/angular_devkit/BUILD.bazel b/packages/core/schematics/utils/tsurge/helpers/angular_devkit/BUILD.bazel new file mode 100644 index 00000000000..e6470463b01 --- /dev/null +++ b/packages/core/schematics/utils/tsurge/helpers/angular_devkit/BUILD.bazel @@ -0,0 +1,14 @@ +load("//tools:defaults.bzl", "ts_library") + +package(default_visibility = ["//packages/core/schematics/ng-generate:__subpackages__"]) + +ts_library( + name = "angular_devkit", + srcs = glob(["**/*.ts"]), + deps = [ + "//packages/compiler-cli/src/ngtsc/file_system", + "@npm//@angular-devkit/core", + "@npm//@angular-devkit/schematics", + "@npm//@types/node", + ], +) diff --git a/packages/core/schematics/utils/tsurge/helpers/angular_devkit/devkit_filesystem.ts b/packages/core/schematics/utils/tsurge/helpers/angular_devkit/devkit_filesystem.ts new file mode 100644 index 00000000000..5af3aa3b7e5 --- /dev/null +++ b/packages/core/schematics/utils/tsurge/helpers/angular_devkit/devkit_filesystem.ts @@ -0,0 +1,200 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + +*/ +import { + normalize, + join, + extname, + relative, + dirname, + Path as DevkitAbsPath, + PathFragment as DevkitPathFragment, + resolve, +} from '@angular-devkit/core'; +import {DirEntry, FileEntry, Tree} from '@angular-devkit/schematics'; +import { + AbsoluteFsPath, + FileStats, + FileSystem, + PathSegment, + PathString, +} from '@angular/compiler-cli/src/ngtsc/file_system'; +import * as path from 'node:path'; + +/** + * Angular compiler file system implementation that leverages an + * CLI schematic virtual file tree. + */ +export class DevkitMigrationFilesystem implements FileSystem { + constructor(private readonly tree: Tree) {} + + extname(path: AbsoluteFsPath | PathSegment): string { + return extname(path as string as DevkitAbsPath | DevkitPathFragment); + } + + isRoot(path: AbsoluteFsPath): boolean { + return (path as string as DevkitAbsPath) === normalize('/'); + } + + isRooted(path: string): boolean { + return this.normalize(path).startsWith('/'); + } + + dirname(file: T): T { + return this.normalize(dirname(file as DevkitAbsPath | DevkitPathFragment)) as string as T; + } + + join(basePath: T, ...paths: string[]): T { + return this.normalize( + join(basePath as DevkitAbsPath | DevkitPathFragment, ...paths), + ) as string as T; + } + + relative(from: T, to: T): PathSegment | AbsoluteFsPath { + return this.normalize( + relative( + from as DevkitAbsPath | DevkitPathFragment, + to as DevkitAbsPath | DevkitPathFragment, + ), + ) as string as PathSegment | AbsoluteFsPath; + } + + basename(filePath: string, extension?: string): PathSegment { + return path.basename(filePath, extension) as PathSegment; + } + + normalize(path: T): T { + return normalize(path as string) as string as T; + } + + resolve(...paths: string[]): AbsoluteFsPath { + const normalizedPaths = paths.map((p) => normalize(p)); + // In dev-kit, the NodeJS working directory should never be + // considered, so `/` is the last resort over `cwd`. + return this.normalize( + path.resolve(normalize('/'), ...normalizedPaths), + ) as string as AbsoluteFsPath; + } + + pwd(): AbsoluteFsPath { + return '/' as AbsoluteFsPath; + } + + isCaseSensitive(): boolean { + return true; + } + + exists(path: AbsoluteFsPath): boolean { + return this.tree.exists(path); + } + + readFile(path: AbsoluteFsPath): string { + return this.tree.readText(path); + } + + readFileBuffer(path: AbsoluteFsPath): Uint8Array { + const buffer = this.tree.read(path); + if (buffer === null) { + throw new Error(`File does not exist: ${path}`); + } + return buffer; + } + + readdir(path: AbsoluteFsPath): PathSegment[] { + const dir = this.tree.getDir(path); + return [ + ...(dir.subdirs as string[] as PathSegment[]), + ...(dir.subfiles as string[] as PathSegment[]), + ]; + } + + lstat(path: AbsoluteFsPath): FileStats { + const stat = statPath(this.tree, path); + if (stat === null) { + throw new Error(`File does not exist for "lstat": ${path}`); + } + return stat; + } + stat(path: AbsoluteFsPath): FileStats { + const stat = statPath(this.tree, path); + if (stat === null) { + throw new Error(`File does not exist for "stat": ${path}`); + } + return stat; + } + + realpath(filePath: AbsoluteFsPath): AbsoluteFsPath { + return filePath; + } + + getDefaultLibLocation(): AbsoluteFsPath { + return 'node_modules/typescript/lib' as AbsoluteFsPath; + } + + ensureDir(path: AbsoluteFsPath): void { + // Migrations should compute replacements and not write directly. + throw new Error('DevkitFilesystem#ensureDir is not supported.'); + } + + writeFile(path: AbsoluteFsPath, data: string | Uint8Array): void { + // Migrations should compute replacements and not write directly. + throw new Error('DevkitFilesystem#writeFile is not supported.'); + } + + removeFile(path: AbsoluteFsPath): void { + // Migrations should compute replacements and not write directly. + throw new Error('DevkitFilesystem#removeFile is not supported.'); + } + + copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { + // Migrations should compute replacements and not write directly. + throw new Error('DevkitFilesystem#copyFile is not supported.'); + } + + moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { + // Migrations should compute replacements and not write directly. + throw new Error('DevkitFilesystem#moveFile is not supported.'); + } + + removeDeep(path: AbsoluteFsPath): void { + // Migrations should compute replacements and not write directly. + throw new Error('DevkitFilesystem#removeDeep is not supported.'); + } + + chdir(_path: AbsoluteFsPath): void { + throw new Error('FileSystem#chdir is not supported.'); + } + + symlink(): void { + throw new Error('FileSystem#symlink is not supported.'); + } +} + +/** Stats the given path in the virtual tree. */ +function statPath(tree: Tree, path: AbsoluteFsPath): FileStats | null { + let fileInfo: FileEntry | null = null; + let dirInfo: DirEntry | null = null; + try { + fileInfo = tree.get(path); + } catch (e) { + if ((e as any).constructor.name === 'PathIsDirectoryException') { + dirInfo = tree.getDir(path); + } else { + throw e; + } + } + + if (fileInfo !== null || dirInfo !== null) { + return { + isDirectory: () => dirInfo !== null, + isFile: () => fileInfo !== null, + isSymbolicLink: () => false, + }; + } + return null; +}