Updated the files.

This commit is contained in:
Batuhan Berk Başoğlu 2024-02-08 19:38:41 -05:00
parent 1553e6b971
commit 753967d4f5
23418 changed files with 3784666 additions and 0 deletions

View file

@ -0,0 +1,78 @@
/**
* @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.io/license
*/
/// <reference types="@types/node/url" />
/// <reference types="@types/node/ts4.8/url" />
import { BaseException } from '@angular-devkit/core';
import { Observable } from 'rxjs';
import { Url } from 'url';
import { MergeStrategy } from '../tree/interface';
import { Workflow } from '../workflow/interface';
import { Collection, CollectionDescription, Engine, EngineHost, ExecutionOptions, Schematic, SchematicContext, SchematicDescription, Source, TaskConfiguration, TaskId, TaskInfo, TypedSchematicContext } from './interface';
export declare class UnknownUrlSourceProtocol extends BaseException {
constructor(url: string);
}
export declare class UnknownCollectionException extends BaseException {
constructor(name: string);
}
export declare class CircularCollectionException extends BaseException {
constructor(name: string);
}
export declare class UnknownSchematicException extends BaseException {
constructor(name: string, collection: CollectionDescription<{}>);
}
export declare class PrivateSchematicException extends BaseException {
constructor(name: string, collection: CollectionDescription<{}>);
}
export declare class SchematicEngineConflictingException extends BaseException {
constructor();
}
export declare class UnregisteredTaskException extends BaseException {
constructor(name: string, schematic?: SchematicDescription<{}, {}>);
}
export declare class UnknownTaskDependencyException extends BaseException {
constructor(id: TaskId);
}
export declare class CollectionImpl<CollectionT extends object, SchematicT extends object> implements Collection<CollectionT, SchematicT> {
private _description;
private _engine;
readonly baseDescriptions?: CollectionDescription<CollectionT>[] | undefined;
constructor(_description: CollectionDescription<CollectionT>, _engine: SchematicEngine<CollectionT, SchematicT>, baseDescriptions?: CollectionDescription<CollectionT>[] | undefined);
get description(): CollectionDescription<CollectionT>;
get name(): string;
createSchematic(name: string, allowPrivate?: boolean): Schematic<CollectionT, SchematicT>;
listSchematicNames(includeHidden?: boolean): string[];
}
export declare class TaskScheduler {
private _context;
private _queue;
private _taskIds;
private static _taskIdCounter;
constructor(_context: SchematicContext);
private _calculatePriority;
private _mapDependencies;
schedule<T extends object>(taskConfiguration: TaskConfiguration<T>): TaskId;
finalize(): ReadonlyArray<TaskInfo>;
}
export declare class SchematicEngine<CollectionT extends object, SchematicT extends object> implements Engine<CollectionT, SchematicT> {
private _host;
protected _workflow?: Workflow | undefined;
private _collectionCache;
private _schematicCache;
private _taskSchedulers;
constructor(_host: EngineHost<CollectionT, SchematicT>, _workflow?: Workflow | undefined);
get workflow(): Workflow | null;
get defaultMergeStrategy(): MergeStrategy;
createCollection(name: string, requester?: Collection<CollectionT, SchematicT>): Collection<CollectionT, SchematicT>;
private _createCollectionDescription;
createContext(schematic: Schematic<CollectionT, SchematicT>, parent?: Partial<TypedSchematicContext<CollectionT, SchematicT>>, executionOptions?: Partial<ExecutionOptions>): TypedSchematicContext<CollectionT, SchematicT>;
createSchematic(name: string, collection: Collection<CollectionT, SchematicT>, allowPrivate?: boolean): Schematic<CollectionT, SchematicT>;
listSchematicNames(collection: Collection<CollectionT, SchematicT>, includeHidden?: boolean): string[];
transformOptions<OptionT extends object, ResultT extends object>(schematic: Schematic<CollectionT, SchematicT>, options: OptionT, context?: TypedSchematicContext<CollectionT, SchematicT>): Observable<ResultT>;
createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionT, SchematicT>): Source;
executePostTasks(): Observable<void>;
}

View file

@ -0,0 +1,300 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchematicEngine = exports.TaskScheduler = exports.CollectionImpl = exports.UnknownTaskDependencyException = exports.UnregisteredTaskException = exports.SchematicEngineConflictingException = exports.PrivateSchematicException = exports.UnknownSchematicException = exports.CircularCollectionException = exports.UnknownCollectionException = exports.UnknownUrlSourceProtocol = void 0;
const core_1 = require("@angular-devkit/core");
const rxjs_1 = require("rxjs");
const interface_1 = require("../tree/interface");
const null_1 = require("../tree/null");
const static_1 = require("../tree/static");
const schematic_1 = require("./schematic");
class UnknownUrlSourceProtocol extends core_1.BaseException {
constructor(url) {
super(`Unknown Protocol on url "${url}".`);
}
}
exports.UnknownUrlSourceProtocol = UnknownUrlSourceProtocol;
class UnknownCollectionException extends core_1.BaseException {
constructor(name) {
super(`Unknown collection "${name}".`);
}
}
exports.UnknownCollectionException = UnknownCollectionException;
class CircularCollectionException extends core_1.BaseException {
constructor(name) {
super(`Circular collection reference "${name}".`);
}
}
exports.CircularCollectionException = CircularCollectionException;
class UnknownSchematicException extends core_1.BaseException {
constructor(name, collection) {
super(`Schematic "${name}" not found in collection "${collection.name}".`);
}
}
exports.UnknownSchematicException = UnknownSchematicException;
class PrivateSchematicException extends core_1.BaseException {
constructor(name, collection) {
super(`Schematic "${name}" not found in collection "${collection.name}".`);
}
}
exports.PrivateSchematicException = PrivateSchematicException;
class SchematicEngineConflictingException extends core_1.BaseException {
constructor() {
super(`A schematic was called from a different engine as its parent.`);
}
}
exports.SchematicEngineConflictingException = SchematicEngineConflictingException;
class UnregisteredTaskException extends core_1.BaseException {
constructor(name, schematic) {
const addendum = schematic ? ` in schematic "${schematic.name}"` : '';
super(`Unregistered task "${name}"${addendum}.`);
}
}
exports.UnregisteredTaskException = UnregisteredTaskException;
class UnknownTaskDependencyException extends core_1.BaseException {
constructor(id) {
super(`Unknown task dependency [ID: ${id.id}].`);
}
}
exports.UnknownTaskDependencyException = UnknownTaskDependencyException;
class CollectionImpl {
_description;
_engine;
baseDescriptions;
constructor(_description, _engine, baseDescriptions) {
this._description = _description;
this._engine = _engine;
this.baseDescriptions = baseDescriptions;
}
get description() {
return this._description;
}
get name() {
return this.description.name || '<unknown>';
}
createSchematic(name, allowPrivate = false) {
return this._engine.createSchematic(name, this, allowPrivate);
}
listSchematicNames(includeHidden) {
return this._engine.listSchematicNames(this, includeHidden);
}
}
exports.CollectionImpl = CollectionImpl;
class TaskScheduler {
_context;
_queue = new core_1.PriorityQueue((x, y) => x.priority - y.priority);
_taskIds = new Map();
static _taskIdCounter = 1;
constructor(_context) {
this._context = _context;
}
_calculatePriority(dependencies) {
if (dependencies.size === 0) {
return 0;
}
const prio = [...dependencies].reduce((prio, task) => prio + task.priority, 1);
return prio;
}
_mapDependencies(dependencies) {
if (!dependencies) {
return new Set();
}
const tasks = dependencies.map((dep) => {
const task = this._taskIds.get(dep);
if (!task) {
throw new UnknownTaskDependencyException(dep);
}
return task;
});
return new Set(tasks);
}
schedule(taskConfiguration) {
const dependencies = this._mapDependencies(taskConfiguration.dependencies);
const priority = this._calculatePriority(dependencies);
const task = {
id: TaskScheduler._taskIdCounter++,
priority,
configuration: taskConfiguration,
context: this._context,
};
this._queue.push(task);
const id = { id: task.id };
this._taskIds.set(id, task);
return id;
}
finalize() {
const tasks = this._queue.toArray();
this._queue.clear();
this._taskIds.clear();
return tasks;
}
}
exports.TaskScheduler = TaskScheduler;
class SchematicEngine {
_host;
_workflow;
_collectionCache = new Map();
_schematicCache = new WeakMap();
_taskSchedulers = new Array();
constructor(_host, _workflow) {
this._host = _host;
this._workflow = _workflow;
}
get workflow() {
return this._workflow || null;
}
get defaultMergeStrategy() {
return this._host.defaultMergeStrategy || interface_1.MergeStrategy.Default;
}
createCollection(name, requester) {
let collection = this._collectionCache.get(name);
if (collection) {
return collection;
}
const [description, bases] = this._createCollectionDescription(name, requester?.description);
collection = new CollectionImpl(description, this, bases);
this._collectionCache.set(name, collection);
this._schematicCache.set(collection, new Map());
return collection;
}
_createCollectionDescription(name, requester, parentNames) {
const description = this._host.createCollectionDescription(name, requester);
if (!description) {
throw new UnknownCollectionException(name);
}
if (parentNames && parentNames.has(description.name)) {
throw new CircularCollectionException(name);
}
const bases = new Array();
if (description.extends) {
parentNames = (parentNames || new Set()).add(description.name);
for (const baseName of description.extends) {
const [base, baseBases] = this._createCollectionDescription(baseName, description, new Set(parentNames));
bases.unshift(base, ...baseBases);
}
}
return [description, bases];
}
createContext(schematic, parent, executionOptions) {
// Check for inconsistencies.
if (parent && parent.engine && parent.engine !== this) {
throw new SchematicEngineConflictingException();
}
let interactive = true;
if (executionOptions && executionOptions.interactive != undefined) {
interactive = executionOptions.interactive;
}
else if (parent && parent.interactive != undefined) {
interactive = parent.interactive;
}
let context = {
debug: (parent && parent.debug) || false,
engine: this,
logger: (parent && parent.logger && parent.logger.createChild(schematic.description.name)) ||
new core_1.logging.NullLogger(),
schematic,
strategy: parent && parent.strategy !== undefined ? parent.strategy : this.defaultMergeStrategy,
interactive,
addTask,
};
const maybeNewContext = this._host.transformContext(context);
if (maybeNewContext) {
context = maybeNewContext;
}
const taskScheduler = new TaskScheduler(context);
const host = this._host;
this._taskSchedulers.push(taskScheduler);
function addTask(task, dependencies) {
const config = task.toConfiguration();
if (!host.hasTaskExecutor(config.name)) {
throw new UnregisteredTaskException(config.name, schematic.description);
}
config.dependencies = config.dependencies || [];
if (dependencies) {
config.dependencies.unshift(...dependencies);
}
return taskScheduler.schedule(config);
}
return context;
}
createSchematic(name, collection, allowPrivate = false) {
const schematicMap = this._schematicCache.get(collection);
let schematic = schematicMap?.get(name);
if (schematic) {
return schematic;
}
let collectionDescription = collection.description;
let description = this._host.createSchematicDescription(name, collection.description);
if (!description) {
if (collection.baseDescriptions) {
for (const base of collection.baseDescriptions) {
description = this._host.createSchematicDescription(name, base);
if (description) {
collectionDescription = base;
break;
}
}
}
if (!description) {
// Report the error for the top level schematic collection
throw new UnknownSchematicException(name, collection.description);
}
}
if (description.private && !allowPrivate) {
throw new PrivateSchematicException(name, collection.description);
}
const factory = this._host.getSchematicRuleFactory(description, collectionDescription);
schematic = new schematic_1.SchematicImpl(description, factory, collection, this);
schematicMap?.set(name, schematic);
return schematic;
}
listSchematicNames(collection, includeHidden) {
const names = this._host.listSchematicNames(collection.description, includeHidden);
if (collection.baseDescriptions) {
for (const base of collection.baseDescriptions) {
names.push(...this._host.listSchematicNames(base, includeHidden));
}
}
// remove duplicates
return [...new Set(names)].sort();
}
transformOptions(schematic, options, context) {
return this._host.transformOptions(schematic.description, options, context);
}
createSourceFromUrl(url, context) {
switch (url.protocol) {
case 'null:':
return () => new null_1.NullTree();
case 'empty:':
return () => (0, static_1.empty)();
default:
const hostSource = this._host.createSourceFromUrl(url, context);
if (!hostSource) {
throw new UnknownUrlSourceProtocol(url.toString());
}
return hostSource;
}
}
executePostTasks() {
const executors = new Map();
const taskObservable = (0, rxjs_1.from)(this._taskSchedulers).pipe((0, rxjs_1.concatMap)((scheduler) => scheduler.finalize()), (0, rxjs_1.concatMap)((task) => {
const { name, options } = task.configuration;
const executor = executors.get(name);
if (executor) {
return executor(options, task.context);
}
return this._host.createTaskExecutor(name).pipe((0, rxjs_1.concatMap)((executor) => {
executors.set(name, executor);
return executor(options, task.context);
}));
}));
return taskObservable;
}
}
exports.SchematicEngine = SchematicEngine;

View file

@ -0,0 +1,10 @@
/**
* @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.io/license
*/
export * from './engine';
export * from './interface';
export * from './schematic';

View file

@ -0,0 +1,26 @@
"use strict";
/**
* @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.io/license
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./engine"), exports);
__exportStar(require("./interface"), exports);
__exportStar(require("./schematic"), exports);

View file

@ -0,0 +1,156 @@
/**
* @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.io/license
*/
/// <reference types="@types/node/url" />
/// <reference types="@types/node/ts4.8/url" />
import { logging } from '@angular-devkit/core';
import { Observable } from 'rxjs';
import { Url } from 'url';
import { FileEntry, MergeStrategy, Tree } from '../tree/interface';
import { Workflow } from '../workflow/interface';
export interface TaskConfiguration<T = {}> {
name: string;
dependencies?: Array<TaskId>;
options?: T;
}
export interface TaskConfigurationGenerator<T = {}> {
toConfiguration(): TaskConfiguration<T>;
}
export type TaskExecutor<T = {}> = (options: T | undefined, context: SchematicContext) => Promise<void> | Observable<void>;
export interface TaskExecutorFactory<T> {
readonly name: string;
create(options?: T): Promise<TaskExecutor> | Observable<TaskExecutor>;
}
export interface TaskId {
readonly id: number;
}
export interface TaskInfo {
readonly id: number;
readonly priority: number;
readonly configuration: TaskConfiguration;
readonly context: SchematicContext;
}
export interface ExecutionOptions {
scope: string;
interactive: boolean;
}
/**
* The description (metadata) of a collection. This type contains every information the engine
* needs to run. The CollectionMetadataT type parameter contains additional metadata that you
* want to store while remaining type-safe.
*/
export type CollectionDescription<CollectionMetadataT extends object> = CollectionMetadataT & {
readonly name: string;
readonly extends?: string[];
};
/**
* The description (metadata) of a schematic. This type contains every information the engine
* needs to run. The SchematicMetadataT and CollectionMetadataT type parameters contain additional
* metadata that you want to store while remaining type-safe.
*/
export type SchematicDescription<CollectionMetadataT extends object, SchematicMetadataT extends object> = SchematicMetadataT & {
readonly collection: CollectionDescription<CollectionMetadataT>;
readonly name: string;
readonly private?: boolean;
readonly hidden?: boolean;
};
/**
* The Host for the Engine. Specifically, the piece of the tooling responsible for resolving
* collections and schematics descriptions. The SchematicMetadataT and CollectionMetadataT type
* parameters contain additional metadata that you want to store while remaining type-safe.
*/
export interface EngineHost<CollectionMetadataT extends object, SchematicMetadataT extends object> {
createCollectionDescription(name: string, requester?: CollectionDescription<CollectionMetadataT>): CollectionDescription<CollectionMetadataT>;
listSchematicNames(collection: CollectionDescription<CollectionMetadataT>, includeHidden?: boolean): string[];
createSchematicDescription(name: string, collection: CollectionDescription<CollectionMetadataT>): SchematicDescription<CollectionMetadataT, SchematicMetadataT> | null;
getSchematicRuleFactory<OptionT extends object>(schematic: SchematicDescription<CollectionMetadataT, SchematicMetadataT>, collection: CollectionDescription<CollectionMetadataT>): RuleFactory<OptionT>;
createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Source | null;
transformOptions<OptionT extends object, ResultT extends object>(schematic: SchematicDescription<CollectionMetadataT, SchematicMetadataT>, options: OptionT, context?: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Observable<ResultT>;
transformContext(context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): TypedSchematicContext<CollectionMetadataT, SchematicMetadataT> | void;
createTaskExecutor(name: string): Observable<TaskExecutor>;
hasTaskExecutor(name: string): boolean;
readonly defaultMergeStrategy?: MergeStrategy;
}
/**
* The root Engine for creating and running schematics and collections. Everything related to
* a schematic execution starts from this interface.
*
* CollectionMetadataT is, by default, a generic Collection metadata type. This is used throughout
* the engine typings so that you can use a type that's merged into descriptions, while being
* type-safe.
*
* SchematicMetadataT is a type that contains additional typing for the Schematic Description.
*/
export interface Engine<CollectionMetadataT extends object, SchematicMetadataT extends object> {
createCollection(name: string, requester?: Collection<CollectionMetadataT, SchematicMetadataT>): Collection<CollectionMetadataT, SchematicMetadataT>;
createContext(schematic: Schematic<CollectionMetadataT, SchematicMetadataT>, parent?: Partial<TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>>, executionOptions?: Partial<ExecutionOptions>): TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>;
createSchematic(name: string, collection: Collection<CollectionMetadataT, SchematicMetadataT>): Schematic<CollectionMetadataT, SchematicMetadataT>;
createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Source;
transformOptions<OptionT extends object, ResultT extends object>(schematic: Schematic<CollectionMetadataT, SchematicMetadataT>, options: OptionT, context?: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Observable<ResultT>;
executePostTasks(): Observable<void>;
readonly defaultMergeStrategy: MergeStrategy;
readonly workflow: Workflow | null;
}
/**
* A Collection as created by the Engine. This should be used by the tool to create schematics,
* or by rules to create other schematics as well.
*/
export interface Collection<CollectionMetadataT extends object, SchematicMetadataT extends object> {
readonly description: CollectionDescription<CollectionMetadataT>;
readonly baseDescriptions?: Array<CollectionDescription<CollectionMetadataT>>;
createSchematic(name: string, allowPrivate?: boolean): Schematic<CollectionMetadataT, SchematicMetadataT>;
listSchematicNames(includeHidden?: boolean): string[];
}
/**
* A Schematic as created by the Engine. This should be used by the tool to execute the main
* schematics, or by rules to execute other schematics as well.
*/
export interface Schematic<CollectionMetadataT extends object, SchematicMetadataT extends object> {
readonly description: SchematicDescription<CollectionMetadataT, SchematicMetadataT>;
readonly collection: Collection<CollectionMetadataT, SchematicMetadataT>;
call<OptionT extends object>(options: OptionT, host: Observable<Tree>, parentContext?: Partial<TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>>, executionOptions?: Partial<ExecutionOptions>): Observable<Tree>;
}
/**
* A SchematicContext. Contains information necessary for Schematics to execute some rules, for
* example when using another schematics, as we need the engine and collection.
*/
export interface TypedSchematicContext<CollectionMetadataT extends object, SchematicMetadataT extends object> {
readonly debug: boolean;
readonly engine: Engine<CollectionMetadataT, SchematicMetadataT>;
readonly logger: logging.LoggerApi;
readonly schematic: Schematic<CollectionMetadataT, SchematicMetadataT>;
readonly strategy: MergeStrategy;
readonly interactive: boolean;
addTask<T extends object>(task: TaskConfigurationGenerator<T>, dependencies?: Array<TaskId>): TaskId;
}
/**
* This is used by the Schematics implementations in order to avoid needing to have typing from
* the tooling. Schematics are not specific to a tool.
*/
export type SchematicContext = TypedSchematicContext<{}, {}>;
/**
* A rule factory, which is normally the way schematics are implemented. Returned by the tooling
* after loading a schematic description.
*/
export type RuleFactory<T extends object> = (options: T) => Rule;
/**
* A FileOperator applies changes synchronously to a FileEntry. An async operator returns
* asynchronously. We separate them so that the type system can catch early errors.
*/
export type FileOperator = (entry: FileEntry) => FileEntry | null;
export type AsyncFileOperator = (tree: FileEntry) => Observable<FileEntry | null>;
/**
* A source is a function that generates a Tree from a specific context. A rule transforms a tree
* into another tree from a specific context. In both cases, an Observable can be returned if
* the source or the rule are asynchronous. Only the last Tree generated in the observable will
* be used though.
*
* We obfuscate the context of Source and Rule because the schematic implementation should not
* know which types is the schematic or collection metadata, as they are both tooling specific.
*/
export type Source = (context: SchematicContext) => Tree | Observable<Tree>;
export type Rule = (tree: Tree, context: SchematicContext) => Tree | Observable<Tree> | Rule | Promise<void | Rule> | void;

View file

@ -0,0 +1,9 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,24 @@
/**
* @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.io/license
*/
import { BaseException } from '@angular-devkit/core';
import { Observable } from 'rxjs';
import { Tree } from '../tree/interface';
import { Collection, Engine, ExecutionOptions, RuleFactory, Schematic, SchematicDescription, TypedSchematicContext } from './interface';
export declare class InvalidSchematicsNameException extends BaseException {
constructor(name: string);
}
export declare class SchematicImpl<CollectionT extends object, SchematicT extends object> implements Schematic<CollectionT, SchematicT> {
private _description;
private _factory;
private _collection;
private _engine;
constructor(_description: SchematicDescription<CollectionT, SchematicT>, _factory: RuleFactory<{}>, _collection: Collection<CollectionT, SchematicT>, _engine: Engine<CollectionT, SchematicT>);
get description(): SchematicDescription<CollectionT, SchematicT>;
get collection(): Collection<CollectionT, SchematicT>;
call<OptionT extends object>(options: OptionT, host: Observable<Tree>, parentContext?: Partial<TypedSchematicContext<CollectionT, SchematicT>>, executionOptions?: Partial<ExecutionOptions>): Observable<Tree>;
}

View file

@ -0,0 +1,70 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchematicImpl = exports.InvalidSchematicsNameException = void 0;
const core_1 = require("@angular-devkit/core");
const rxjs_1 = require("rxjs");
const call_1 = require("../rules/call");
const scoped_1 = require("../tree/scoped");
class InvalidSchematicsNameException extends core_1.BaseException {
constructor(name) {
super(`Schematics has invalid name: "${name}".`);
}
}
exports.InvalidSchematicsNameException = InvalidSchematicsNameException;
class SchematicImpl {
_description;
_factory;
_collection;
_engine;
constructor(_description, _factory, _collection, _engine) {
this._description = _description;
this._factory = _factory;
this._collection = _collection;
this._engine = _engine;
if (!_description.name.match(/^[-@/_.a-zA-Z0-9]+$/)) {
throw new InvalidSchematicsNameException(_description.name);
}
}
get description() {
return this._description;
}
get collection() {
return this._collection;
}
call(options, host, parentContext, executionOptions) {
const context = this._engine.createContext(this, parentContext, executionOptions);
return host.pipe((0, rxjs_1.first)(), (0, rxjs_1.concatMap)((tree) => this._engine
.transformOptions(this, options, context)
.pipe((0, rxjs_1.map)((o) => [tree, o]))), (0, rxjs_1.concatMap)(([tree, transformedOptions]) => {
let input;
let scoped = false;
if (executionOptions && executionOptions.scope) {
scoped = true;
input = new scoped_1.ScopedTree(tree, executionOptions.scope);
}
else {
input = tree;
}
return (0, call_1.callRule)(this._factory(transformedOptions), input, context).pipe((0, rxjs_1.map)((output) => {
if (output === input) {
return tree;
}
else if (scoped) {
tree.merge(output);
return tree;
}
else {
return output;
}
}));
}));
}
}
exports.SchematicImpl = SchematicImpl;

View file

@ -0,0 +1,31 @@
/**
* @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.io/license
*/
import { BaseException } from '@angular-devkit/core';
export declare class SchematicsException extends BaseException {
}
export declare class FileDoesNotExistException extends BaseException {
constructor(path: string);
}
export declare class FileAlreadyExistException extends BaseException {
constructor(path: string);
}
export declare class ContentHasMutatedException extends BaseException {
constructor(path: string);
}
export declare class InvalidUpdateRecordException extends BaseException {
constructor();
}
export declare class MergeConflictException extends BaseException {
constructor(path: string);
}
export declare class UnsuccessfulWorkflowExecution extends BaseException {
constructor();
}
export declare class UnimplementedException extends BaseException {
constructor();
}

View file

@ -0,0 +1,58 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnimplementedException = exports.UnsuccessfulWorkflowExecution = exports.MergeConflictException = exports.InvalidUpdateRecordException = exports.ContentHasMutatedException = exports.FileAlreadyExistException = exports.FileDoesNotExistException = exports.SchematicsException = void 0;
const core_1 = require("@angular-devkit/core");
// Used by schematics to throw exceptions.
class SchematicsException extends core_1.BaseException {
}
exports.SchematicsException = SchematicsException;
// Exceptions
class FileDoesNotExistException extends core_1.BaseException {
constructor(path) {
super(`Path "${path}" does not exist.`);
}
}
exports.FileDoesNotExistException = FileDoesNotExistException;
class FileAlreadyExistException extends core_1.BaseException {
constructor(path) {
super(`Path "${path}" already exist.`);
}
}
exports.FileAlreadyExistException = FileAlreadyExistException;
class ContentHasMutatedException extends core_1.BaseException {
constructor(path) {
super(`Content at path "${path}" has changed between the start and the end of an update.`);
}
}
exports.ContentHasMutatedException = ContentHasMutatedException;
class InvalidUpdateRecordException extends core_1.BaseException {
constructor() {
super(`Invalid record instance.`);
}
}
exports.InvalidUpdateRecordException = InvalidUpdateRecordException;
class MergeConflictException extends core_1.BaseException {
constructor(path) {
super(`A merge conflicted on path "${path}".`);
}
}
exports.MergeConflictException = MergeConflictException;
class UnsuccessfulWorkflowExecution extends core_1.BaseException {
constructor() {
super('Workflow did not execute successfully.');
}
}
exports.UnsuccessfulWorkflowExecution = UnsuccessfulWorkflowExecution;
class UnimplementedException extends core_1.BaseException {
constructor() {
super('This function is unimplemented.');
}
}
exports.UnimplementedException = UnimplementedException;

View file

@ -0,0 +1,9 @@
/**
* @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.io/license
*/
import { JsonObject, JsonValue, schema } from '@angular-devkit/core';
export declare function formatValidator(data: JsonValue, dataSchema: JsonObject, formats: schema.SchemaFormat[]): Promise<schema.SchemaValidatorResult>;

View file

@ -0,0 +1,20 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatValidator = void 0;
const core_1 = require("@angular-devkit/core");
async function formatValidator(data, dataSchema, formats) {
const registry = new core_1.schema.CoreSchemaRegistry();
for (const format of formats) {
registry.addFormat(format);
}
const validator = await registry.compile(dataSchema);
return validator(data);
}
exports.formatValidator = formatValidator;

View file

@ -0,0 +1,9 @@
/**
* @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.io/license
*/
import { schema } from '@angular-devkit/core';
export declare const htmlSelectorFormat: schema.SchemaFormat;

View file

@ -0,0 +1,44 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.htmlSelectorFormat = void 0;
// As per https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
// * Without mandatory `-` as the application prefix will generally cover its inclusion
// * And an allowance for upper alpha characters
// NOTE: This should eventually be broken out into two formats: full and partial (allows for prefix)
const unicodeRanges = [
[0xc0, 0xd6],
[0xd8, 0xf6],
[0xf8, 0x37d],
[0x37f, 0x1fff],
[0x200c, 0x200d],
[0x203f, 0x2040],
[0x2070, 0x218f],
[0x2c00, 0x2fef],
[0x3001, 0xd7ff],
[0xf900, 0xfdcf],
[0xfdf0, 0xfffd],
[0x10000, 0xeffff],
];
function isValidElementName(name) {
let regex = '^[a-zA-Z][';
regex += '-.0-9_a-zA-Z\\u{B7}';
for (const range of unicodeRanges) {
regex += `\\u{${range[0].toString(16)}}-\\u{${range[1].toString(16)}}`;
}
regex += ']*$';
return new RegExp(regex, 'u').test(name);
}
exports.htmlSelectorFormat = {
name: 'html-selector',
formatter: {
async: false,
validate: (name) => typeof name === 'string' && isValidElementName(name),
},
};

View file

@ -0,0 +1,11 @@
/**
* @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.io/license
*/
import { schema } from '@angular-devkit/core';
export { htmlSelectorFormat } from './html-selector';
export { pathFormat } from './path';
export declare const standardFormats: schema.SchemaFormat[];

View file

@ -0,0 +1,17 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.standardFormats = exports.pathFormat = exports.htmlSelectorFormat = void 0;
const html_selector_1 = require("./html-selector");
const path_1 = require("./path");
var html_selector_2 = require("./html-selector");
Object.defineProperty(exports, "htmlSelectorFormat", { enumerable: true, get: function () { return html_selector_2.htmlSelectorFormat; } });
var path_2 = require("./path");
Object.defineProperty(exports, "pathFormat", { enumerable: true, get: function () { return path_2.pathFormat; } });
exports.standardFormats = [html_selector_1.htmlSelectorFormat, path_1.pathFormat];

View file

@ -0,0 +1,9 @@
/**
* @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.io/license
*/
import { schema } from '@angular-devkit/core';
export declare const pathFormat: schema.SchemaFormat;

View file

@ -0,0 +1,24 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.pathFormat = void 0;
const core_1 = require("@angular-devkit/core");
exports.pathFormat = {
name: 'path',
formatter: {
async: false,
validate: (path) => {
// Check path is normalized already.
return path === (0, core_1.normalize)(path);
// TODO: check if path is valid (is that just checking if it's normalized?)
// TODO: check path is from root of schematics even if passed absolute
// TODO: error out if path is outside of host
},
},
};

View file

@ -0,0 +1,41 @@
/**
* @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.io/license
*/
import { strings } from '@angular-devkit/core';
import * as formats from './formats/index';
import { FilePredicate, MergeStrategy, Tree as TreeInterface } from './tree/interface';
import * as workflow from './workflow/index';
export { SchematicsException } from './exception/exception';
export * from './tree/action';
export * from './engine/index';
export * from './exception/exception';
export * from './tree/interface';
export * from './rules/base';
export * from './rules/call';
export * from './rules/move';
export * from './rules/random';
export * from './rules/schematic';
export * from './rules/template';
export * from './rules/url';
export * from './tree/delegate';
export * from './tree/empty';
export * from './tree/host-tree';
export { UpdateRecorder } from './tree/interface';
export * from './engine/schematic';
export * from './sink/dryrun';
export * from './sink/host';
export * from './sink/sink';
export { formats, strings, workflow };
export interface TreeConstructor {
empty(): TreeInterface;
branch(tree: TreeInterface): TreeInterface;
merge(tree: TreeInterface, other: TreeInterface, strategy?: MergeStrategy): TreeInterface;
partition(tree: TreeInterface, predicate: FilePredicate<boolean>): [TreeInterface, TreeInterface];
optimize(tree: TreeInterface): TreeInterface;
}
export type Tree = TreeInterface;
export declare const Tree: TreeConstructor;

81
my-app/node_modules/@angular-devkit/schematics/src/index.js generated vendored Executable file
View file

@ -0,0 +1,81 @@
"use strict";
/**
* @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.io/license
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Tree = exports.workflow = exports.strings = exports.formats = exports.SchematicsException = void 0;
const core_1 = require("@angular-devkit/core");
Object.defineProperty(exports, "strings", { enumerable: true, get: function () { return core_1.strings; } });
const formats = __importStar(require("./formats/index"));
exports.formats = formats;
const interface_1 = require("./tree/interface");
const static_1 = require("./tree/static");
const workflow = __importStar(require("./workflow/index"));
exports.workflow = workflow;
var exception_1 = require("./exception/exception");
Object.defineProperty(exports, "SchematicsException", { enumerable: true, get: function () { return exception_1.SchematicsException; } });
__exportStar(require("./tree/action"), exports);
__exportStar(require("./engine/index"), exports);
__exportStar(require("./exception/exception"), exports);
__exportStar(require("./tree/interface"), exports);
__exportStar(require("./rules/base"), exports);
__exportStar(require("./rules/call"), exports);
__exportStar(require("./rules/move"), exports);
__exportStar(require("./rules/random"), exports);
__exportStar(require("./rules/schematic"), exports);
__exportStar(require("./rules/template"), exports);
__exportStar(require("./rules/url"), exports);
__exportStar(require("./tree/delegate"), exports);
__exportStar(require("./tree/empty"), exports);
__exportStar(require("./tree/host-tree"), exports);
__exportStar(require("./engine/schematic"), exports);
__exportStar(require("./sink/dryrun"), exports);
__exportStar(require("./sink/host"), exports);
__exportStar(require("./sink/sink"), exports);
exports.Tree = {
empty() {
return (0, static_1.empty)();
},
branch(tree) {
return (0, static_1.branch)(tree);
},
merge(tree, other, strategy = interface_1.MergeStrategy.Default) {
return (0, static_1.merge)(tree, other, strategy);
},
partition(tree, predicate) {
return (0, static_1.partition)(tree, predicate);
},
optimize(tree) {
return tree;
},
};

View file

@ -0,0 +1,38 @@
/**
* @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.io/license
*/
import { FileOperator, Rule, Source } from '../engine/interface';
import { FilePredicate, MergeStrategy, Tree } from '../tree/interface';
/**
* A Source that returns an tree as its single value.
*/
export declare function source(tree: Tree): Source;
/**
* A source that returns an empty tree.
*/
export declare function empty(): Source;
/**
* Chain multiple rules into a single rule.
*/
export declare function chain(rules: Iterable<Rule> | AsyncIterable<Rule>): Rule;
/**
* Apply multiple rules to a source, and returns the source transformed.
*/
export declare function apply(source: Source, rules: Rule[]): Source;
/**
* Merge an input tree with the source passed in.
*/
export declare function mergeWith(source: Source, strategy?: MergeStrategy): Rule;
export declare function noop(): Rule;
export declare function filter(predicate: FilePredicate<boolean>): Rule;
export declare function asSource(rule: Rule): Source;
export declare function branchAndMerge(rule: Rule, strategy?: MergeStrategy): Rule;
export declare function when(predicate: FilePredicate<boolean>, operator: FileOperator): FileOperator;
export declare function partitionApplyMerge(predicate: FilePredicate<boolean>, ruleYes: Rule, ruleNo?: Rule): Rule;
export declare function forEach(operator: FileOperator): Rule;
export declare function composeFileOperators(operators: FileOperator[]): FileOperator;
export declare function applyToSubtree(path: string, rules: Rule[]): Rule;

View file

@ -0,0 +1,158 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyToSubtree = exports.composeFileOperators = exports.forEach = exports.partitionApplyMerge = exports.when = exports.branchAndMerge = exports.asSource = exports.filter = exports.noop = exports.mergeWith = exports.apply = exports.chain = exports.empty = exports.source = void 0;
const rxjs_1 = require("rxjs");
const exception_1 = require("../exception/exception");
const host_tree_1 = require("../tree/host-tree");
const interface_1 = require("../tree/interface");
const scoped_1 = require("../tree/scoped");
const static_1 = require("../tree/static");
const call_1 = require("./call");
/**
* A Source that returns an tree as its single value.
*/
function source(tree) {
return () => tree;
}
exports.source = source;
/**
* A source that returns an empty tree.
*/
function empty() {
return () => (0, static_1.empty)();
}
exports.empty = empty;
/**
* Chain multiple rules into a single rule.
*/
function chain(rules) {
return async (initialTree, context) => {
let intermediateTree;
for await (const rule of rules) {
intermediateTree = (0, call_1.callRule)(rule, intermediateTree ?? initialTree, context);
}
return () => intermediateTree;
};
}
exports.chain = chain;
/**
* Apply multiple rules to a source, and returns the source transformed.
*/
function apply(source, rules) {
return (context) => (0, call_1.callRule)(chain(rules), (0, call_1.callSource)(source, context), context);
}
exports.apply = apply;
/**
* Merge an input tree with the source passed in.
*/
function mergeWith(source, strategy = interface_1.MergeStrategy.Default) {
return (tree, context) => {
return (0, call_1.callSource)(source, context).pipe((0, rxjs_1.map)((sourceTree) => tree.merge(sourceTree, strategy || context.strategy)), (0, rxjs_1.mapTo)(tree));
};
}
exports.mergeWith = mergeWith;
function noop() {
return () => { };
}
exports.noop = noop;
function filter(predicate) {
return (tree) => {
if (host_tree_1.HostTree.isHostTree(tree)) {
return new host_tree_1.FilterHostTree(tree, predicate);
}
else {
throw new exception_1.SchematicsException('Tree type is not supported.');
}
};
}
exports.filter = filter;
function asSource(rule) {
return (context) => (0, call_1.callRule)(rule, (0, static_1.empty)(), context);
}
exports.asSource = asSource;
function branchAndMerge(rule, strategy = interface_1.MergeStrategy.Default) {
return (tree, context) => {
return (0, call_1.callRule)(rule, tree.branch(), context).pipe((0, rxjs_1.map)((branch) => tree.merge(branch, strategy || context.strategy)), (0, rxjs_1.mapTo)(tree));
};
}
exports.branchAndMerge = branchAndMerge;
function when(predicate, operator) {
return (entry) => {
if (predicate(entry.path, entry)) {
return operator(entry);
}
else {
return entry;
}
};
}
exports.when = when;
function partitionApplyMerge(predicate, ruleYes, ruleNo) {
return (tree, context) => {
const [yes, no] = (0, static_1.partition)(tree, predicate);
return (0, rxjs_1.concat)((0, call_1.callRule)(ruleYes, yes, context), (0, call_1.callRule)(ruleNo || noop(), no, context)).pipe((0, rxjs_1.toArray)(), (0, rxjs_1.map)(([yesTree, noTree]) => {
yesTree.merge(noTree, context.strategy);
return yesTree;
}));
};
}
exports.partitionApplyMerge = partitionApplyMerge;
function forEach(operator) {
return (tree) => {
tree.visit((path, entry) => {
if (!entry) {
return;
}
const newEntry = operator(entry);
if (newEntry === entry) {
return;
}
if (newEntry === null) {
tree.delete(path);
return;
}
if (newEntry.path != path) {
tree.rename(path, newEntry.path);
}
if (!newEntry.content.equals(entry.content)) {
tree.overwrite(newEntry.path, newEntry.content);
}
});
};
}
exports.forEach = forEach;
function composeFileOperators(operators) {
return (entry) => {
let current = entry;
for (const op of operators) {
current = op(current);
if (current === null) {
// Deleted, just return.
return null;
}
}
return current;
};
}
exports.composeFileOperators = composeFileOperators;
function applyToSubtree(path, rules) {
return (tree, context) => {
const scoped = new scoped_1.ScopedTree(tree, path);
return (0, call_1.callRule)(chain(rules), scoped, context).pipe((0, rxjs_1.map)((result) => {
if (result === scoped) {
return tree;
}
else {
throw new exception_1.SchematicsException('Original tree must be returned from all rules when using "applyToSubtree".');
}
}));
};
}
exports.applyToSubtree = applyToSubtree;

View file

@ -0,0 +1,22 @@
/**
* @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.io/license
*/
import { BaseException } from '@angular-devkit/core';
import { Observable } from 'rxjs';
import { Rule, SchematicContext, Source } from '../engine/interface';
import { Tree } from '../tree/interface';
/**
* When a rule or source returns an invalid value.
*/
export declare class InvalidRuleResultException extends BaseException {
constructor(value?: {});
}
export declare class InvalidSourceResultException extends BaseException {
constructor(value?: {});
}
export declare function callSource(source: Source, context: SchematicContext): Observable<Tree>;
export declare function callRule(rule: Rule, input: Tree | Observable<Tree>, context: SchematicContext): Observable<Tree>;

View file

@ -0,0 +1,92 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.callRule = exports.callSource = exports.InvalidSourceResultException = exports.InvalidRuleResultException = void 0;
const core_1 = require("@angular-devkit/core");
const rxjs_1 = require("rxjs");
const interface_1 = require("../tree/interface");
function _getTypeOfResult(value) {
if (value === undefined) {
return 'undefined';
}
else if (value === null) {
return 'null';
}
else if (typeof value == 'function') {
return `Function()`;
}
else if (typeof value != 'object') {
return `${typeof value}(${JSON.stringify(value)})`;
}
else {
if (Object.getPrototypeOf(value) == Object) {
return `Object(${JSON.stringify(value)})`;
}
else if (value.constructor) {
return `Instance of class ${value.constructor.name}`;
}
else {
return 'Unknown Object';
}
}
}
/**
* When a rule or source returns an invalid value.
*/
class InvalidRuleResultException extends core_1.BaseException {
constructor(value) {
super(`Invalid rule result: ${_getTypeOfResult(value)}.`);
}
}
exports.InvalidRuleResultException = InvalidRuleResultException;
class InvalidSourceResultException extends core_1.BaseException {
constructor(value) {
super(`Invalid source result: ${_getTypeOfResult(value)}.`);
}
}
exports.InvalidSourceResultException = InvalidSourceResultException;
function callSource(source, context) {
return (0, rxjs_1.defer)(async () => {
let result = source(context);
if ((0, rxjs_1.isObservable)(result)) {
result = await (0, rxjs_1.lastValueFrom)(result.pipe((0, rxjs_1.defaultIfEmpty)(undefined)));
}
if (result && interface_1.TreeSymbol in result) {
return result;
}
throw new InvalidSourceResultException(result);
});
}
exports.callSource = callSource;
function callRule(rule, input, context) {
if ((0, rxjs_1.isObservable)(input)) {
return input.pipe((0, rxjs_1.mergeMap)((inputTree) => callRuleAsync(rule, inputTree, context)));
}
else {
return (0, rxjs_1.defer)(() => callRuleAsync(rule, input, context));
}
}
exports.callRule = callRule;
async function callRuleAsync(rule, tree, context) {
let result = await rule(tree, context);
while (typeof result === 'function') {
// This is considered a Rule, chain the rule and return its output.
result = await result(tree, context);
}
if (typeof result === 'undefined') {
return tree;
}
if ((0, rxjs_1.isObservable)(result)) {
result = await (0, rxjs_1.lastValueFrom)(result.pipe((0, rxjs_1.defaultIfEmpty)(tree)));
}
if (result && interface_1.TreeSymbol in result) {
return result;
}
throw new InvalidRuleResultException(result);
}

View file

@ -0,0 +1,9 @@
/**
* @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.io/license
*/
import { Rule } from '../engine/interface';
export declare function move(from: string, to?: string): Rule;

View file

@ -0,0 +1,37 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.move = void 0;
const core_1 = require("@angular-devkit/core");
const base_1 = require("./base");
function move(from, to) {
if (to === undefined) {
to = from;
from = '/';
}
const fromPath = (0, core_1.normalize)('/' + from);
const toPath = (0, core_1.normalize)('/' + to);
if (fromPath === toPath) {
return base_1.noop;
}
return (tree) => {
if (tree.exists(fromPath)) {
// fromPath is a file
tree.rename(fromPath, toPath);
}
else {
// fromPath is a directory
tree.getDir(fromPath).visit((path) => {
tree.rename(path, (0, core_1.join)(toPath, path.slice(fromPath.length)));
});
}
return tree;
};
}
exports.move = move;

View file

@ -0,0 +1,14 @@
/**
* @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.io/license
*/
import { Source } from '../engine/interface';
export interface RandomOptions {
root?: string;
multi?: boolean | number;
multiFiles?: boolean | number;
}
export default function (options: RandomOptions): Source;

View file

@ -0,0 +1,40 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
const host_tree_1 = require("../tree/host-tree");
function generateStringOfLength(l) {
return new Array(l)
.fill(0)
.map((_x) => {
return 'abcdefghijklmnopqrstuvwxyz'[Math.floor(Math.random() * 26)];
})
.join('');
}
function random(from, to) {
return Math.floor(Math.random() * (to - from)) + from;
}
function default_1(options) {
return () => {
const root = 'root' in options ? options.root : '/';
const map = new host_tree_1.HostTree();
const nbFiles = 'multiFiles' in options
? typeof options.multiFiles == 'number'
? options.multiFiles
: random(2, 12)
: 1;
for (let i = 0; i < nbFiles; i++) {
const path = 'a/b/c/d/e/f'.slice(Math.random() * 10);
const fileName = generateStringOfLength(20);
const content = generateStringOfLength(100);
map.create(root + '/' + path + '/' + fileName, content);
}
return map;
};
}
exports.default = default_1;

View file

@ -0,0 +1,23 @@
/**
* @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.io/license
*/
import { ExecutionOptions, Rule } from '../engine/interface';
/**
* Run a schematic from a separate collection.
*
* @param collectionName The name of the collection that contains the schematic to run.
* @param schematicName The name of the schematic to run.
* @param options The options to pass as input to the RuleFactory.
*/
export declare function externalSchematic<OptionT extends object>(collectionName: string, schematicName: string, options: OptionT, executionOptions?: Partial<ExecutionOptions>): Rule;
/**
* Run a schematic from the same collection.
*
* @param schematicName The name of the schematic to run.
* @param options The options to pass as input to the RuleFactory.
*/
export declare function schematic<OptionT extends object>(schematicName: string, options: OptionT, executionOptions?: Partial<ExecutionOptions>): Rule;

View file

@ -0,0 +1,50 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.schematic = exports.externalSchematic = void 0;
const rxjs_1 = require("rxjs");
const interface_1 = require("../tree/interface");
const static_1 = require("../tree/static");
/**
* Run a schematic from a separate collection.
*
* @param collectionName The name of the collection that contains the schematic to run.
* @param schematicName The name of the schematic to run.
* @param options The options to pass as input to the RuleFactory.
*/
function externalSchematic(collectionName, schematicName, options, executionOptions) {
return (input, context) => {
const collection = context.engine.createCollection(collectionName, context.schematic.collection);
const schematic = collection.createSchematic(schematicName);
return schematic.call(options, (0, rxjs_1.of)((0, static_1.branch)(input)), context, executionOptions).pipe((0, rxjs_1.last)(), (0, rxjs_1.map)((x) => {
input.merge(x, interface_1.MergeStrategy.AllowOverwriteConflict);
return input;
}));
};
}
exports.externalSchematic = externalSchematic;
/**
* Run a schematic from the same collection.
*
* @param schematicName The name of the schematic to run.
* @param options The options to pass as input to the RuleFactory.
*/
function schematic(schematicName, options, executionOptions) {
return (input, context) => {
const collection = context.schematic.collection;
const schematic = collection.createSchematic(schematicName, true);
return schematic.call(options, (0, rxjs_1.of)((0, static_1.branch)(input)), context, executionOptions).pipe((0, rxjs_1.last)(), (0, rxjs_1.map)((x) => {
// We allow overwrite conflict here because they're the only merge conflict we particularly
// don't want to deal with; the input tree might have an OVERWRITE which the sub
input.merge(x, interface_1.MergeStrategy.AllowOverwriteConflict);
return input;
}));
};
}
exports.schematic = schematic;

View file

@ -0,0 +1,39 @@
/**
* @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.io/license
*/
import { BaseException } from '@angular-devkit/core';
import { FileOperator, Rule } from '../engine/interface';
export declare const TEMPLATE_FILENAME_RE: RegExp;
export declare class OptionIsNotDefinedException extends BaseException {
constructor(name: string);
}
export declare class UnknownPipeException extends BaseException {
constructor(name: string);
}
export declare class InvalidPipeException extends BaseException {
constructor(name: string);
}
export type PathTemplateValue = boolean | string | number | undefined;
export type PathTemplatePipeFunction = (x: string) => PathTemplateValue;
export type PathTemplateData = {
[key: string]: PathTemplateValue | PathTemplateData | PathTemplatePipeFunction;
};
export interface PathTemplateOptions {
interpolationStart: string;
interpolationEnd: string;
pipeSeparator?: string;
}
export declare function applyContentTemplate<T>(options: T): FileOperator;
export declare function contentTemplate<T>(options: T): Rule;
export declare function applyPathTemplate<T extends PathTemplateData>(data: T, options?: PathTemplateOptions): FileOperator;
export declare function pathTemplate<T extends PathTemplateData>(options: T): Rule;
/**
* Remove every `.template` suffix from file names.
*/
export declare function renameTemplateFiles(): Rule;
export declare function template<T extends object>(options: T): Rule;
export declare function applyTemplates<T extends object>(options: T): Rule;

View file

@ -0,0 +1,160 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyTemplates = exports.template = exports.renameTemplateFiles = exports.pathTemplate = exports.applyPathTemplate = exports.contentTemplate = exports.applyContentTemplate = exports.InvalidPipeException = exports.UnknownPipeException = exports.OptionIsNotDefinedException = exports.TEMPLATE_FILENAME_RE = void 0;
const core_1 = require("@angular-devkit/core");
const node_os_1 = require("node:os");
const base_1 = require("./base");
exports.TEMPLATE_FILENAME_RE = /\.template$/;
class OptionIsNotDefinedException extends core_1.BaseException {
constructor(name) {
super(`Option "${name}" is not defined.`);
}
}
exports.OptionIsNotDefinedException = OptionIsNotDefinedException;
class UnknownPipeException extends core_1.BaseException {
constructor(name) {
super(`Pipe "${name}" is not defined.`);
}
}
exports.UnknownPipeException = UnknownPipeException;
class InvalidPipeException extends core_1.BaseException {
constructor(name) {
super(`Pipe "${name}" is invalid.`);
}
}
exports.InvalidPipeException = InvalidPipeException;
const decoder = new TextDecoder('utf-8', { fatal: true });
function applyContentTemplate(options) {
return (entry) => {
const { path, content } = entry;
try {
const decodedContent = decoder.decode(content).replace(/\r?\n/g, node_os_1.EOL);
return {
path,
content: Buffer.from((0, core_1.template)(decodedContent, {})(options)),
};
}
catch (e) {
if (e.code === 'ERR_ENCODING_INVALID_ENCODED_DATA') {
return entry;
}
throw e;
}
};
}
exports.applyContentTemplate = applyContentTemplate;
function contentTemplate(options) {
return (0, base_1.forEach)(applyContentTemplate(options));
}
exports.contentTemplate = contentTemplate;
function applyPathTemplate(data, options = {
interpolationStart: '__',
interpolationEnd: '__',
pipeSeparator: '@',
}) {
const is = options.interpolationStart;
const ie = options.interpolationEnd;
const isL = is.length;
const ieL = ie.length;
return (entry) => {
let path = entry.path;
const content = entry.content;
const original = path;
let start = path.indexOf(is);
// + 1 to have at least a length 1 name. `____` is not valid.
let end = path.indexOf(ie, start + isL + 1);
while (start != -1 && end != -1) {
const match = path.substring(start + isL, end);
let replacement = data[match];
if (!options.pipeSeparator) {
if (typeof replacement == 'function') {
replacement = replacement.call(data, original);
}
if (replacement === undefined) {
throw new OptionIsNotDefinedException(match);
}
}
else {
const [name, ...pipes] = match.split(options.pipeSeparator);
replacement = data[name];
if (typeof replacement == 'function') {
replacement = replacement.call(data, original);
}
if (replacement === undefined) {
throw new OptionIsNotDefinedException(name);
}
replacement = pipes.reduce((acc, pipe) => {
if (!pipe) {
return acc;
}
if (!(pipe in data)) {
throw new UnknownPipeException(pipe);
}
if (typeof data[pipe] != 'function') {
throw new InvalidPipeException(pipe);
}
// Coerce to string.
return '' + data[pipe](acc);
}, '' + replacement);
}
path = path.substring(0, start) + replacement + path.substring(end + ieL);
start = path.indexOf(options.interpolationStart);
// See above.
end = path.indexOf(options.interpolationEnd, start + isL + 1);
}
return { path: (0, core_1.normalize)(path), content };
};
}
exports.applyPathTemplate = applyPathTemplate;
function pathTemplate(options) {
return (0, base_1.forEach)(applyPathTemplate(options));
}
exports.pathTemplate = pathTemplate;
/**
* Remove every `.template` suffix from file names.
*/
function renameTemplateFiles() {
return (0, base_1.forEach)((entry) => {
if (entry.path.match(exports.TEMPLATE_FILENAME_RE)) {
return {
content: entry.content,
path: (0, core_1.normalize)(entry.path.replace(exports.TEMPLATE_FILENAME_RE, '')),
};
}
else {
return entry;
}
});
}
exports.renameTemplateFiles = renameTemplateFiles;
function template(options) {
return (0, base_1.chain)([
contentTemplate(options),
// Force cast to PathTemplateData. We need the type for the actual pathTemplate() call,
// but in this case we cannot do anything as contentTemplate are more permissive.
// Since values are coerced to strings in PathTemplates it will be fine in the end.
pathTemplate(options),
]);
}
exports.template = template;
function applyTemplates(options) {
return (0, base_1.forEach)((0, base_1.when)((path) => path.endsWith('.template'), (0, base_1.composeFileOperators)([
applyContentTemplate(options),
// See above for this weird cast.
applyPathTemplate(options),
(entry) => {
return {
content: entry.content,
path: entry.path.replace(exports.TEMPLATE_FILENAME_RE, ''),
};
},
])));
}
exports.applyTemplates = applyTemplates;

View file

@ -0,0 +1,9 @@
/**
* @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.io/license
*/
import { Source } from '../engine/interface';
export declare function url(urlString: string): Source;

View file

@ -0,0 +1,16 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.url = void 0;
const url_1 = require("url");
function url(urlString) {
const url = (0, url_1.parse)(urlString);
return (context) => context.engine.createSourceFromUrl(url, context)(context);
}
exports.url = url;

View file

@ -0,0 +1,49 @@
/**
* @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.io/license
*/
import { virtualFs } from '@angular-devkit/core';
import { Observable, Subject } from 'rxjs';
import { HostSink } from './host';
export interface DryRunErrorEvent {
kind: 'error';
description: 'alreadyExist' | 'doesNotExist';
path: string;
}
export interface DryRunDeleteEvent {
kind: 'delete';
path: string;
}
export interface DryRunCreateEvent {
kind: 'create';
path: string;
content: Buffer;
}
export interface DryRunUpdateEvent {
kind: 'update';
path: string;
content: Buffer;
}
export interface DryRunRenameEvent {
kind: 'rename';
path: string;
to: string;
}
export type DryRunEvent = DryRunErrorEvent | DryRunDeleteEvent | DryRunCreateEvent | DryRunUpdateEvent | DryRunRenameEvent;
export declare class DryRunSink extends HostSink {
protected _subject: Subject<DryRunEvent>;
protected _fileDoesNotExistExceptionSet: Set<string>;
protected _fileAlreadyExistExceptionSet: Set<string>;
readonly reporter: Observable<DryRunEvent>;
/**
* @param {host} dir The host to use to output. This should be scoped.
* @param {boolean} force Whether to force overwriting files that already exist.
*/
constructor(host: virtualFs.Host, force?: boolean);
protected _fileAlreadyExistException(path: string): void;
protected _fileDoesNotExistException(path: string): void;
_done(): Observable<void>;
}

View file

@ -0,0 +1,80 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DryRunSink = void 0;
const core_1 = require("@angular-devkit/core");
const node_1 = require("@angular-devkit/core/node");
const rxjs_1 = require("rxjs");
const host_1 = require("./host");
class DryRunSink extends host_1.HostSink {
_subject = new rxjs_1.Subject();
_fileDoesNotExistExceptionSet = new Set();
_fileAlreadyExistExceptionSet = new Set();
reporter = this._subject.asObservable();
constructor(host, force = false) {
super(typeof host == 'string'
? new core_1.virtualFs.ScopedHost(new node_1.NodeJsSyncHost(), (0, core_1.normalize)(host))
: host, force);
}
_fileAlreadyExistException(path) {
this._fileAlreadyExistExceptionSet.add(path);
}
_fileDoesNotExistException(path) {
this._fileDoesNotExistExceptionSet.add(path);
}
_done() {
this._fileAlreadyExistExceptionSet.forEach((path) => {
this._subject.next({
kind: 'error',
description: 'alreadyExist',
path,
});
});
this._fileDoesNotExistExceptionSet.forEach((path) => {
this._subject.next({
kind: 'error',
description: 'doesNotExist',
path,
});
});
this._filesToDelete.forEach((path) => {
// Check if this is a renaming.
for (const [from] of this._filesToRename) {
if (from == path) {
// The event is sent later on.
return;
}
}
this._subject.next({ kind: 'delete', path });
});
this._filesToRename.forEach(([path, to]) => {
this._subject.next({ kind: 'rename', path, to });
});
this._filesToCreate.forEach((content, path) => {
// Check if this is a renaming.
for (const [, to] of this._filesToRename) {
if (to == path) {
// The event is sent later on.
return;
}
}
if (this._fileAlreadyExistExceptionSet.has(path) ||
this._fileDoesNotExistExceptionSet.has(path)) {
return;
}
this._subject.next({ kind: 'create', path, content });
});
this._filesToUpdate.forEach((content, path) => {
this._subject.next({ kind: 'update', path, content });
});
this._subject.complete();
return (0, rxjs_1.of)(undefined);
}
}
exports.DryRunSink = DryRunSink;

View file

@ -0,0 +1,27 @@
/**
* @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.io/license
*/
import { Path, virtualFs } from '@angular-devkit/core';
import { Observable } from 'rxjs';
import { CreateFileAction } from '../tree/action';
import { SimpleSinkBase } from './sink';
export declare class HostSink extends SimpleSinkBase {
protected _host: virtualFs.Host;
protected _force: boolean;
protected _filesToDelete: Set<Path>;
protected _filesToRename: Set<[Path, Path]>;
protected _filesToCreate: Map<Path, Buffer>;
protected _filesToUpdate: Map<Path, Buffer>;
constructor(_host: virtualFs.Host, _force?: boolean);
protected _validateCreateAction(action: CreateFileAction): Observable<void>;
protected _validateFileExists(p: Path): Observable<boolean>;
protected _overwriteFile(path: Path, content: Buffer): Observable<void>;
protected _createFile(path: Path, content: Buffer): Observable<void>;
protected _renameFile(from: Path, to: Path): Observable<void>;
protected _deleteFile(path: Path): Observable<void>;
_done(): Observable<void>;
}

View file

@ -0,0 +1,72 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.HostSink = void 0;
const rxjs_1 = require("rxjs");
const sink_1 = require("./sink");
class HostSink extends sink_1.SimpleSinkBase {
_host;
_force;
_filesToDelete = new Set();
_filesToRename = new Set();
_filesToCreate = new Map();
_filesToUpdate = new Map();
constructor(_host, _force = false) {
super();
this._host = _host;
this._force = _force;
}
_validateCreateAction(action) {
return this._force ? rxjs_1.EMPTY : super._validateCreateAction(action);
}
_validateFileExists(p) {
if (this._filesToCreate.has(p) || this._filesToUpdate.has(p)) {
return (0, rxjs_1.of)(true);
}
if (this._filesToDelete.has(p)) {
return (0, rxjs_1.of)(false);
}
for (const [from, to] of this._filesToRename.values()) {
switch (p) {
case from:
return (0, rxjs_1.of)(false);
case to:
return (0, rxjs_1.of)(true);
}
}
return this._host.exists(p);
}
_overwriteFile(path, content) {
this._filesToUpdate.set(path, content);
return rxjs_1.EMPTY;
}
_createFile(path, content) {
this._filesToCreate.set(path, content);
return rxjs_1.EMPTY;
}
_renameFile(from, to) {
this._filesToRename.add([from, to]);
return rxjs_1.EMPTY;
}
_deleteFile(path) {
if (this._filesToCreate.has(path)) {
this._filesToCreate.delete(path);
this._filesToUpdate.delete(path);
}
else {
this._filesToDelete.add(path);
}
return rxjs_1.EMPTY;
}
_done() {
// Really commit everything to the actual filesystem.
return (0, rxjs_1.concat)((0, rxjs_1.from)([...this._filesToDelete.values()]).pipe((0, rxjs_1.concatMap)((path) => this._host.delete(path))), (0, rxjs_1.from)([...this._filesToRename.entries()]).pipe((0, rxjs_1.concatMap)(([_, [path, to]]) => this._host.rename(path, to))), (0, rxjs_1.from)([...this._filesToCreate.entries()]).pipe((0, rxjs_1.concatMap)(([path, buffer]) => this._host.write(path, buffer))), (0, rxjs_1.from)([...this._filesToUpdate.entries()]).pipe((0, rxjs_1.concatMap)(([path, buffer]) => this._host.write(path, buffer)))).pipe((0, rxjs_1.reduce)(() => { }));
}
}
exports.HostSink = HostSink;

View file

@ -0,0 +1,34 @@
/**
* @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.io/license
*/
import { Observable } from 'rxjs';
import { Action, CreateFileAction, DeleteFileAction, OverwriteFileAction, RenameFileAction } from '../tree/action';
import { Tree } from '../tree/interface';
export interface Sink {
commit(tree: Tree): Observable<void>;
}
export declare abstract class SimpleSinkBase implements Sink {
preCommitAction: (action: Action) => void | Action | PromiseLike<Action> | Observable<Action>;
postCommitAction: (action: Action) => void | Observable<void>;
preCommit: () => void | Observable<void>;
postCommit: () => void | Observable<void>;
protected abstract _validateFileExists(p: string): Observable<boolean>;
protected abstract _overwriteFile(path: string, content: Buffer): Observable<void>;
protected abstract _createFile(path: string, content: Buffer): Observable<void>;
protected abstract _renameFile(path: string, to: string): Observable<void>;
protected abstract _deleteFile(path: string): Observable<void>;
protected abstract _done(): Observable<void>;
protected _fileAlreadyExistException(path: string): void;
protected _fileDoesNotExistException(path: string): void;
protected _validateOverwriteAction(action: OverwriteFileAction): Observable<void>;
protected _validateCreateAction(action: CreateFileAction): Observable<void>;
protected _validateRenameAction(action: RenameFileAction): Observable<void>;
protected _validateDeleteAction(action: DeleteFileAction): Observable<void>;
validateSingleAction(action: Action): Observable<void>;
commitSingleAction(action: Action): Observable<void>;
commit(tree: Tree): Observable<void>;
}

View file

@ -0,0 +1,113 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SimpleSinkBase = void 0;
const rxjs_1 = require("rxjs");
const exception_1 = require("../exception/exception");
const action_1 = require("../tree/action");
const Noop = function () { };
class SimpleSinkBase {
preCommitAction = Noop;
postCommitAction = Noop;
preCommit = Noop;
postCommit = Noop;
_fileAlreadyExistException(path) {
throw new exception_1.FileAlreadyExistException(path);
}
_fileDoesNotExistException(path) {
throw new exception_1.FileDoesNotExistException(path);
}
_validateOverwriteAction(action) {
return this._validateFileExists(action.path).pipe((0, rxjs_1.map)((b) => {
if (!b) {
this._fileDoesNotExistException(action.path);
}
}));
}
_validateCreateAction(action) {
return this._validateFileExists(action.path).pipe((0, rxjs_1.map)((b) => {
if (b) {
this._fileAlreadyExistException(action.path);
}
}));
}
_validateRenameAction(action) {
return this._validateFileExists(action.path).pipe((0, rxjs_1.map)((b) => {
if (!b) {
this._fileDoesNotExistException(action.path);
}
}), (0, rxjs_1.mergeMap)(() => this._validateFileExists(action.to)), (0, rxjs_1.map)((b) => {
if (b) {
this._fileAlreadyExistException(action.to);
}
}));
}
_validateDeleteAction(action) {
return this._validateFileExists(action.path).pipe((0, rxjs_1.map)((b) => {
if (!b) {
this._fileDoesNotExistException(action.path);
}
}));
}
validateSingleAction(action) {
switch (action.kind) {
case 'o':
return this._validateOverwriteAction(action);
case 'c':
return this._validateCreateAction(action);
case 'r':
return this._validateRenameAction(action);
case 'd':
return this._validateDeleteAction(action);
default:
throw new action_1.UnknownActionException(action);
}
}
commitSingleAction(action) {
return (0, rxjs_1.concat)(this.validateSingleAction(action), new rxjs_1.Observable((observer) => {
let committed = null;
switch (action.kind) {
case 'o':
committed = this._overwriteFile(action.path, action.content);
break;
case 'c':
committed = this._createFile(action.path, action.content);
break;
case 'r':
committed = this._renameFile(action.path, action.to);
break;
case 'd':
committed = this._deleteFile(action.path);
break;
}
if (committed) {
committed.subscribe(observer);
}
else {
observer.complete();
}
})).pipe((0, rxjs_1.ignoreElements)());
}
commit(tree) {
const actions = (0, rxjs_1.from)(tree.actions);
return (0, rxjs_1.concat)(this.preCommit() || (0, rxjs_1.of)(null), (0, rxjs_1.defer)(() => actions).pipe((0, rxjs_1.concatMap)((action) => {
const maybeAction = this.preCommitAction(action);
if ((0, rxjs_1.isObservable)(maybeAction) || isPromiseLike(maybeAction)) {
return maybeAction;
}
return (0, rxjs_1.of)(maybeAction || action);
}), (0, rxjs_1.concatMap)((action) => {
return (0, rxjs_1.concat)(this.commitSingleAction(action).pipe((0, rxjs_1.ignoreElements)()), (0, rxjs_1.of)(action));
}), (0, rxjs_1.concatMap)((action) => this.postCommitAction(action) || (0, rxjs_1.of)(null))), (0, rxjs_1.defer)(() => this._done()), (0, rxjs_1.defer)(() => this.postCommit() || (0, rxjs_1.of)(null))).pipe((0, rxjs_1.ignoreElements)(), (0, rxjs_1.defaultIfEmpty)(undefined));
}
}
exports.SimpleSinkBase = SimpleSinkBase;
function isPromiseLike(value) {
return !!value && typeof value.then === 'function';
}

View file

@ -0,0 +1,49 @@
/**
* @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.io/license
*/
import { BaseException, Path } from '@angular-devkit/core';
export declare class UnknownActionException extends BaseException {
constructor(action: Action);
}
export type Action = CreateFileAction | OverwriteFileAction | RenameFileAction | DeleteFileAction;
export interface ActionBase {
readonly id: number;
readonly parent: number;
readonly path: Path;
}
export declare class ActionList implements Iterable<Action> {
private _actions;
protected _action(action: Partial<Action>): void;
create(path: Path, content: Buffer): void;
overwrite(path: Path, content: Buffer): void;
rename(path: Path, to: Path): void;
delete(path: Path): void;
optimize(): void;
push(action: Action): void;
get(i: number): Action;
has(action: Action): boolean;
find(predicate: (value: Action) => boolean): Action | null;
forEach(fn: (value: Action, index: number, array: Action[]) => void, thisArg?: {}): void;
get length(): number;
[Symbol.iterator](): IterableIterator<Action>;
}
export declare function isContentAction(action: Action): action is CreateFileAction | OverwriteFileAction;
export interface CreateFileAction extends ActionBase {
readonly kind: 'c';
readonly content: Buffer;
}
export interface OverwriteFileAction extends ActionBase {
readonly kind: 'o';
readonly content: Buffer;
}
export interface RenameFileAction extends ActionBase {
readonly kind: 'r';
readonly to: Path;
}
export interface DeleteFileAction extends ActionBase {
readonly kind: 'd';
}

View file

@ -0,0 +1,137 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.isContentAction = exports.ActionList = exports.UnknownActionException = void 0;
const core_1 = require("@angular-devkit/core");
class UnknownActionException extends core_1.BaseException {
constructor(action) {
super(`Unknown action: "${action.kind}".`);
}
}
exports.UnknownActionException = UnknownActionException;
let _id = 1;
class ActionList {
_actions = [];
_action(action) {
this._actions.push({
...action,
id: _id++,
parent: this._actions[this._actions.length - 1]?.id ?? 0,
});
}
create(path, content) {
this._action({ kind: 'c', path, content });
}
overwrite(path, content) {
this._action({ kind: 'o', path, content });
}
rename(path, to) {
this._action({ kind: 'r', path, to });
}
delete(path) {
this._action({ kind: 'd', path });
}
optimize() {
const toCreate = new Map();
const toRename = new Map();
const toOverwrite = new Map();
const toDelete = new Set();
for (const action of this._actions) {
switch (action.kind) {
case 'c':
toCreate.set(action.path, action.content);
break;
case 'o':
if (toCreate.has(action.path)) {
toCreate.set(action.path, action.content);
}
else {
toOverwrite.set(action.path, action.content);
}
break;
case 'd':
toDelete.add(action.path);
break;
case 'r':
const maybeCreate = toCreate.get(action.path);
const maybeOverwrite = toOverwrite.get(action.path);
if (maybeCreate) {
toCreate.delete(action.path);
toCreate.set(action.to, maybeCreate);
}
if (maybeOverwrite) {
toOverwrite.delete(action.path);
toOverwrite.set(action.to, maybeOverwrite);
}
let maybeRename = undefined;
for (const [from, to] of toRename.entries()) {
if (to == action.path) {
maybeRename = from;
break;
}
}
if (maybeRename) {
toRename.set(maybeRename, action.to);
}
if (!maybeCreate && !maybeOverwrite && !maybeRename) {
toRename.set(action.path, action.to);
}
break;
}
}
this._actions = [];
toDelete.forEach((x) => {
this.delete(x);
});
toRename.forEach((to, from) => {
this.rename(from, to);
});
toCreate.forEach((content, path) => {
this.create(path, content);
});
toOverwrite.forEach((content, path) => {
this.overwrite(path, content);
});
}
push(action) {
this._actions.push(action);
}
get(i) {
return this._actions[i];
}
has(action) {
for (let i = 0; i < this._actions.length; i++) {
const a = this._actions[i];
if (a.id == action.id) {
return true;
}
if (a.id > action.id) {
return false;
}
}
return false;
}
find(predicate) {
return this._actions.find(predicate) || null;
}
forEach(fn, thisArg) {
this._actions.forEach(fn, thisArg);
}
get length() {
return this._actions.length;
}
[Symbol.iterator]() {
return this._actions[Symbol.iterator]();
}
}
exports.ActionList = ActionList;
function isContentAction(action) {
return action.kind == 'c' || action.kind == 'o';
}
exports.isContentAction = isContentAction;

View file

@ -0,0 +1,32 @@
/**
* @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.io/license
*/
import { JsonValue } from '@angular-devkit/core';
import { Action } from './action';
import { DirEntry, FileEntry, FileVisitor, MergeStrategy, Tree, UpdateRecorder } from './interface';
export declare class DelegateTree implements Tree {
protected _other: Tree;
constructor(_other: Tree);
branch(): Tree;
merge(other: Tree, strategy?: MergeStrategy): void;
get root(): DirEntry;
read(path: string): Buffer | null;
readText(path: string): string;
readJson(path: string): JsonValue;
exists(path: string): boolean;
get(path: string): FileEntry | null;
getDir(path: string): DirEntry;
visit(visitor: FileVisitor): void;
overwrite(path: string, content: Buffer | string): void;
beginUpdate(path: string): UpdateRecorder;
commitUpdate(record: UpdateRecorder): void;
create(path: string, content: Buffer | string): void;
delete(path: string): void;
rename(from: string, to: string): void;
apply(action: Action, strategy?: MergeStrategy): void;
get actions(): Action[];
}

View file

@ -0,0 +1,78 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DelegateTree = void 0;
const interface_1 = require("./interface");
class DelegateTree {
_other;
constructor(_other) {
this._other = _other;
}
branch() {
return this._other.branch();
}
merge(other, strategy) {
this._other.merge(other, strategy);
}
get root() {
return this._other.root;
}
// Readonly.
read(path) {
return this._other.read(path);
}
readText(path) {
return this._other.readText(path);
}
readJson(path) {
return this._other.readJson(path);
}
exists(path) {
return this._other.exists(path);
}
get(path) {
return this._other.get(path);
}
getDir(path) {
return this._other.getDir(path);
}
visit(visitor) {
return this._other.visit(visitor);
}
// Change content of host files.
overwrite(path, content) {
return this._other.overwrite(path, content);
}
beginUpdate(path) {
return this._other.beginUpdate(path);
}
commitUpdate(record) {
return this._other.commitUpdate(record);
}
// Structural methods.
create(path, content) {
return this._other.create(path, content);
}
delete(path) {
return this._other.delete(path);
}
rename(from, to) {
return this._other.rename(from, to);
}
apply(action, strategy) {
return this._other.apply(action, strategy);
}
get actions() {
return this._other.actions;
}
[interface_1.TreeSymbol]() {
return this;
}
}
exports.DelegateTree = DelegateTree;

View file

@ -0,0 +1,11 @@
/**
* @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.io/license
*/
import { HostTree } from './host-tree';
export declare class EmptyTree extends HostTree {
constructor();
}

View file

@ -0,0 +1,17 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmptyTree = void 0;
const host_tree_1 = require("./host-tree");
class EmptyTree extends host_tree_1.HostTree {
constructor() {
super();
}
}
exports.EmptyTree = EmptyTree;

View file

@ -0,0 +1,24 @@
/**
* @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.io/license
*/
import { Path } from '@angular-devkit/core';
import { FileEntry } from './interface';
export declare class SimpleFileEntry implements FileEntry {
private _path;
private _content;
constructor(_path: Path, _content: Buffer);
get path(): Path;
get content(): Buffer;
}
export declare class LazyFileEntry implements FileEntry {
private _path;
private _load;
private _content;
constructor(_path: Path, _load: (path?: Path) => Buffer);
get path(): Path;
get content(): Buffer;
}

View file

@ -0,0 +1,41 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.LazyFileEntry = exports.SimpleFileEntry = void 0;
class SimpleFileEntry {
_path;
_content;
constructor(_path, _content) {
this._path = _path;
this._content = _content;
}
get path() {
return this._path;
}
get content() {
return this._content;
}
}
exports.SimpleFileEntry = SimpleFileEntry;
class LazyFileEntry {
_path;
_load;
_content = null;
constructor(_path, _load) {
this._path = _path;
this._load = _load;
}
get path() {
return this._path;
}
get content() {
return this._content || (this._content = this._load(this._path));
}
}
exports.LazyFileEntry = LazyFileEntry;

View file

@ -0,0 +1,64 @@
/**
* @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.io/license
*/
import { JsonValue, Path, PathFragment, virtualFs } from '@angular-devkit/core';
import { Action } from './action';
import { DirEntry, FileEntry, FilePredicate, FileVisitor, MergeStrategy, Tree, UpdateRecorder } from './interface';
export declare class HostDirEntry implements DirEntry {
readonly parent: DirEntry | null;
readonly path: Path;
protected _host: virtualFs.SyncDelegateHost;
protected _tree: Tree;
constructor(parent: DirEntry | null, path: Path, _host: virtualFs.SyncDelegateHost, _tree: Tree);
get subdirs(): PathFragment[];
get subfiles(): PathFragment[];
dir(name: PathFragment): DirEntry;
file(name: PathFragment): FileEntry | null;
visit(visitor: FileVisitor): void;
private getSubfilesRecursively;
}
export declare class HostTree implements Tree {
protected _backend: virtualFs.ReadonlyHost<{}>;
private readonly _id;
private _record;
private _recordSync;
private _ancestry;
private _dirCache;
static isHostTree(tree: Tree): tree is HostTree;
constructor(_backend?: virtualFs.ReadonlyHost<{}>);
protected _normalizePath(path: string): Path;
protected _willCreate(path: Path): boolean;
protected _willOverwrite(path: Path): boolean;
protected _willDelete(path: Path): boolean;
protected _willRename(path: Path): boolean;
branch(): Tree;
private isAncestorOf;
merge(other: Tree, strategy?: MergeStrategy): void;
get root(): DirEntry;
read(path: string): Buffer | null;
readText(path: string): string;
readJson(path: string): JsonValue;
exists(path: string): boolean;
get(path: string): FileEntry | null;
getDir(path: string): DirEntry;
visit(visitor: FileVisitor): void;
overwrite(path: string, content: Buffer | string): void;
beginUpdate(path: string): UpdateRecorder;
commitUpdate(record: UpdateRecorder): void;
create(path: string, content: Buffer | string): void;
delete(path: string): void;
rename(from: string, to: string): void;
apply(action: Action, strategy?: MergeStrategy): void;
private generateActions;
get actions(): Action[];
}
export declare class HostCreateTree extends HostTree {
constructor(host: virtualFs.ReadonlyHost);
}
export declare class FilterHostTree extends HostTree {
constructor(tree: HostTree, filter?: FilePredicate<boolean>);
}

View file

@ -0,0 +1,442 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.FilterHostTree = exports.HostCreateTree = exports.HostTree = exports.HostDirEntry = void 0;
const core_1 = require("@angular-devkit/core");
const jsonc_parser_1 = require("jsonc-parser");
const rxjs_1 = require("rxjs");
const util_1 = require("util");
const exception_1 = require("../exception/exception");
const delegate_1 = require("./delegate");
const entry_1 = require("./entry");
const interface_1 = require("./interface");
const recorder_1 = require("./recorder");
const scoped_1 = require("./scoped");
let _uniqueId = 0;
class HostDirEntry {
parent;
path;
_host;
_tree;
constructor(parent, path, _host, _tree) {
this.parent = parent;
this.path = path;
this._host = _host;
this._tree = _tree;
}
get subdirs() {
return this._host
.list(this.path)
.filter((fragment) => this._host.isDirectory((0, core_1.join)(this.path, fragment)));
}
get subfiles() {
return this._host
.list(this.path)
.filter((fragment) => this._host.isFile((0, core_1.join)(this.path, fragment)));
}
dir(name) {
return this._tree.getDir((0, core_1.join)(this.path, name));
}
file(name) {
return this._tree.get((0, core_1.join)(this.path, name));
}
visit(visitor) {
try {
this.getSubfilesRecursively().forEach((file) => visitor(file.path, file));
}
catch (e) {
if (e !== interface_1.FileVisitorCancelToken) {
throw e;
}
}
}
getSubfilesRecursively() {
function _recurse(entry) {
return entry.subdirs.reduce((files, subdir) => [...files, ..._recurse(entry.dir(subdir))], entry.subfiles.map((subfile) => entry.file(subfile)));
}
return _recurse(this);
}
}
exports.HostDirEntry = HostDirEntry;
class HostTree {
_backend;
_id = --_uniqueId;
_record;
_recordSync;
_ancestry = new Set();
_dirCache = new Map();
[interface_1.TreeSymbol]() {
return this;
}
static isHostTree(tree) {
if (tree instanceof HostTree) {
return true;
}
if (typeof tree === 'object' && typeof tree._ancestry === 'object') {
return true;
}
return false;
}
constructor(_backend = new core_1.virtualFs.Empty()) {
this._backend = _backend;
this._record = new core_1.virtualFs.CordHost(new core_1.virtualFs.SafeReadonlyHost(_backend));
this._recordSync = new core_1.virtualFs.SyncDelegateHost(this._record);
}
_normalizePath(path) {
return (0, core_1.normalize)('/' + path);
}
_willCreate(path) {
return this._record.willCreate(path);
}
_willOverwrite(path) {
return this._record.willOverwrite(path);
}
_willDelete(path) {
return this._record.willDelete(path);
}
_willRename(path) {
return this._record.willRename(path);
}
branch() {
const branchedTree = new HostTree(this._backend);
branchedTree._record = this._record.clone();
branchedTree._recordSync = new core_1.virtualFs.SyncDelegateHost(branchedTree._record);
branchedTree._ancestry = new Set(this._ancestry).add(this._id);
return branchedTree;
}
isAncestorOf(tree) {
if (tree instanceof HostTree) {
return tree._ancestry.has(this._id);
}
if (tree instanceof delegate_1.DelegateTree) {
return this.isAncestorOf(tree._other);
}
if (tree instanceof scoped_1.ScopedTree) {
return this.isAncestorOf(tree._base);
}
return false;
}
merge(other, strategy = interface_1.MergeStrategy.Default) {
if (other === this) {
// Merging with yourself? Tsk tsk. Nothing to do at least.
return;
}
if (this.isAncestorOf(other)) {
// Workaround for merging a branch back into one of its ancestors
// More complete branch point tracking is required to avoid
strategy |= interface_1.MergeStrategy.Overwrite;
}
const creationConflictAllowed = (strategy & interface_1.MergeStrategy.AllowCreationConflict) == interface_1.MergeStrategy.AllowCreationConflict;
const overwriteConflictAllowed = (strategy & interface_1.MergeStrategy.AllowOverwriteConflict) == interface_1.MergeStrategy.AllowOverwriteConflict;
const deleteConflictAllowed = (strategy & interface_1.MergeStrategy.AllowDeleteConflict) == interface_1.MergeStrategy.AllowDeleteConflict;
other.actions.forEach((action) => {
switch (action.kind) {
case 'c': {
const { path, content } = action;
if (this._willCreate(path) || this._willOverwrite(path) || this.exists(path)) {
const existingContent = this.read(path);
if (existingContent && content.equals(existingContent)) {
// Identical outcome; no action required
return;
}
if (!creationConflictAllowed) {
throw new exception_1.MergeConflictException(path);
}
this._record.overwrite(path, content).subscribe();
}
else {
this._record.create(path, content).subscribe();
}
return;
}
case 'o': {
const { path, content } = action;
if (this._willDelete(path) && !overwriteConflictAllowed) {
throw new exception_1.MergeConflictException(path);
}
// Ignore if content is the same (considered the same change).
if (this._willOverwrite(path)) {
const existingContent = this.read(path);
if (existingContent && content.equals(existingContent)) {
// Identical outcome; no action required
return;
}
if (!overwriteConflictAllowed) {
throw new exception_1.MergeConflictException(path);
}
}
// We use write here as merge validation has already been done, and we want to let
// the CordHost do its job.
this._record.write(path, content).subscribe();
return;
}
case 'r': {
const { path, to } = action;
if (this._willDelete(path)) {
throw new exception_1.MergeConflictException(path);
}
if (this._willRename(path)) {
if (this._record.willRenameTo(path, to)) {
// Identical outcome; no action required
return;
}
// No override possible for renaming.
throw new exception_1.MergeConflictException(path);
}
this.rename(path, to);
return;
}
case 'd': {
const { path } = action;
if (this._willDelete(path)) {
// TODO: This should technically check the content (e.g., hash on delete)
// Identical outcome; no action required
return;
}
if (!this.exists(path) && !deleteConflictAllowed) {
throw new exception_1.MergeConflictException(path);
}
this._recordSync.delete(path);
return;
}
}
});
}
get root() {
return this.getDir('/');
}
// Readonly.
read(path) {
const entry = this.get(path);
return entry ? entry.content : null;
}
readText(path) {
const data = this.read(path);
if (data === null) {
throw new exception_1.FileDoesNotExistException(path);
}
const decoder = new util_1.TextDecoder('utf-8', { fatal: true });
try {
// With the `fatal` option enabled, invalid data will throw a TypeError
return decoder.decode(data);
}
catch (e) {
if (e instanceof TypeError) {
throw new Error(`Failed to decode "${path}" as UTF-8 text.`);
}
throw e;
}
}
readJson(path) {
const content = this.readText(path);
const errors = [];
const result = (0, jsonc_parser_1.parse)(content, errors, { allowTrailingComma: true });
// If there is a parse error throw with the error information
if (errors[0]) {
const { error, offset } = errors[0];
throw new Error(`Failed to parse "${path}" as JSON. ${(0, jsonc_parser_1.printParseErrorCode)(error)} at offset: ${offset}.`);
}
return result;
}
exists(path) {
return this._recordSync.isFile(this._normalizePath(path));
}
get(path) {
const p = this._normalizePath(path);
if (this._recordSync.isDirectory(p)) {
throw new core_1.PathIsDirectoryException(p);
}
if (!this._recordSync.exists(p)) {
return null;
}
return new entry_1.LazyFileEntry(p, () => Buffer.from(this._recordSync.read(p)));
}
getDir(path) {
const p = this._normalizePath(path);
if (this._recordSync.isFile(p)) {
throw new core_1.PathIsFileException(p);
}
let maybeCache = this._dirCache.get(p);
if (!maybeCache) {
let parent = (0, core_1.dirname)(p);
if (p === parent) {
parent = null;
}
maybeCache = new HostDirEntry(parent && this.getDir(parent), p, this._recordSync, this);
this._dirCache.set(p, maybeCache);
}
return maybeCache;
}
visit(visitor) {
this.root.visit((path, entry) => {
visitor(path, entry);
});
}
// Change content of host files.
overwrite(path, content) {
const p = this._normalizePath(path);
if (!this._recordSync.exists(p)) {
throw new exception_1.FileDoesNotExistException(p);
}
const c = typeof content == 'string' ? Buffer.from(content) : content;
this._record.overwrite(p, c).subscribe();
}
beginUpdate(path) {
const entry = this.get(path);
if (!entry) {
throw new exception_1.FileDoesNotExistException(path);
}
return recorder_1.UpdateRecorderBase.createFromFileEntry(entry);
}
commitUpdate(record) {
if (record instanceof recorder_1.UpdateRecorderBase) {
const path = record.path;
const entry = this.get(path);
if (!entry) {
throw new exception_1.ContentHasMutatedException(path);
}
else {
const newContent = record.apply(entry.content);
if (!newContent.equals(entry.content)) {
this.overwrite(path, newContent);
}
}
}
else {
throw new exception_1.InvalidUpdateRecordException();
}
}
// Structural methods.
create(path, content) {
const p = this._normalizePath(path);
if (this._recordSync.exists(p)) {
throw new exception_1.FileAlreadyExistException(p);
}
const c = typeof content == 'string' ? Buffer.from(content) : content;
this._record.create(p, c).subscribe();
}
delete(path) {
this._recordSync.delete(this._normalizePath(path));
}
rename(from, to) {
this._recordSync.rename(this._normalizePath(from), this._normalizePath(to));
}
apply(action, strategy) {
throw new exception_1.SchematicsException('Apply not implemented on host trees.');
}
*generateActions() {
for (const record of this._record.records()) {
switch (record.kind) {
case 'create':
yield {
id: this._id,
parent: 0,
kind: 'c',
path: record.path,
content: Buffer.from(record.content),
};
break;
case 'overwrite':
yield {
id: this._id,
parent: 0,
kind: 'o',
path: record.path,
content: Buffer.from(record.content),
};
break;
case 'rename':
yield {
id: this._id,
parent: 0,
kind: 'r',
path: record.from,
to: record.to,
};
break;
case 'delete':
yield {
id: this._id,
parent: 0,
kind: 'd',
path: record.path,
};
break;
}
}
}
get actions() {
// Create a list of all records until we hit our original backend. This is to support branches
// that diverge from each others.
return Array.from(this.generateActions());
}
}
exports.HostTree = HostTree;
class HostCreateTree extends HostTree {
constructor(host) {
super();
const tempHost = new HostTree(host);
tempHost.visit((path) => {
const content = tempHost.read(path);
if (content) {
this.create(path, content);
}
});
}
}
exports.HostCreateTree = HostCreateTree;
class FilterHostTree extends HostTree {
constructor(tree, filter = () => true) {
const newBackend = new core_1.virtualFs.SimpleMemoryHost();
// cast to allow access
const originalBackend = tree._backend;
const recurse = (base) => {
return originalBackend.list(base).pipe((0, rxjs_1.mergeMap)((x) => x), (0, rxjs_1.map)((path) => (0, core_1.join)(base, path)), (0, rxjs_1.concatMap)((path) => {
let isDirectory = false;
originalBackend.isDirectory(path).subscribe((val) => (isDirectory = val));
if (isDirectory) {
return recurse(path);
}
let isFile = false;
originalBackend.isFile(path).subscribe((val) => (isFile = val));
if (!isFile || !filter(path)) {
return rxjs_1.EMPTY;
}
let content = null;
originalBackend.read(path).subscribe((val) => (content = val));
if (!content) {
return rxjs_1.EMPTY;
}
return newBackend.write(path, content);
}));
};
recurse((0, core_1.normalize)('/')).subscribe();
super(newBackend);
for (const action of tree.actions) {
if (!filter(action.path)) {
continue;
}
switch (action.kind) {
case 'c':
this.create(action.path, action.content);
break;
case 'd':
this.delete(action.path);
break;
case 'o':
this.overwrite(action.path, action.content);
break;
case 'r':
this.rename(action.path, action.to);
break;
}
}
}
}
exports.FilterHostTree = FilterHostTree;

View file

@ -0,0 +1,82 @@
/**
* @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.io/license
*/
import { JsonValue, Path, PathFragment } from '@angular-devkit/core';
import { Action } from './action';
export declare enum MergeStrategy {
AllowOverwriteConflict = 2,
AllowCreationConflict = 4,
AllowDeleteConflict = 8,
Default = 0,
Error = 1,
ContentOnly = 2,
Overwrite = 14
}
export declare const FileVisitorCancelToken: symbol;
export type FileVisitor = FilePredicate<void>;
export interface FileEntry {
readonly path: Path;
readonly content: Buffer;
}
export interface DirEntry {
readonly parent: DirEntry | null;
readonly path: Path;
readonly subdirs: PathFragment[];
readonly subfiles: PathFragment[];
dir(name: PathFragment): DirEntry;
file(name: PathFragment): FileEntry | null;
visit(visitor: FileVisitor): void;
}
export interface FilePredicate<T> {
(path: Path, entry?: Readonly<FileEntry> | null): T;
}
export declare const TreeSymbol: symbol;
export interface Tree {
branch(): Tree;
merge(other: Tree, strategy?: MergeStrategy): void;
readonly root: DirEntry;
read(path: string): Buffer | null;
/**
* Reads a file from the Tree as a UTF-8 encoded text file.
*
* @param path The path of the file to read.
* @returns A string containing the contents of the file.
* @throws {@link FileDoesNotExistException} if the file is not found.
* @throws An error if the file contains invalid UTF-8 characters.
*/
readText(path: string): string;
/**
* Reads and parses a file from the Tree as a UTF-8 encoded JSON file.
* Supports parsing JSON (RFC 8259) with the following extensions:
* * Single-line and multi-line JavaScript comments
* * Trailing commas within objects and arrays
*
* @param path The path of the file to read.
* @returns A JsonValue containing the parsed contents of the file.
* @throws {@link FileDoesNotExistException} if the file is not found.
* @throws An error if the file contains invalid UTF-8 characters.
* @throws An error if the file contains malformed JSON.
*/
readJson(path: string): JsonValue;
exists(path: string): boolean;
get(path: string): FileEntry | null;
getDir(path: string): DirEntry;
visit(visitor: FileVisitor): void;
overwrite(path: string, content: Buffer | string): void;
beginUpdate(path: string): UpdateRecorder;
commitUpdate(record: UpdateRecorder): void;
create(path: string, content: Buffer | string): void;
delete(path: string): void;
rename(from: string, to: string): void;
apply(action: Action, strategy?: MergeStrategy): void;
readonly actions: Action[];
}
export interface UpdateRecorder {
insertLeft(index: number, content: Buffer | string): UpdateRecorder;
insertRight(index: number, content: Buffer | string): UpdateRecorder;
remove(index: number, length: number): UpdateRecorder;
}

View file

@ -0,0 +1,47 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TreeSymbol = exports.FileVisitorCancelToken = exports.MergeStrategy = void 0;
var MergeStrategy;
(function (MergeStrategy) {
MergeStrategy[MergeStrategy["AllowOverwriteConflict"] = 2] = "AllowOverwriteConflict";
MergeStrategy[MergeStrategy["AllowCreationConflict"] = 4] = "AllowCreationConflict";
MergeStrategy[MergeStrategy["AllowDeleteConflict"] = 8] = "AllowDeleteConflict";
// Uses the default strategy.
MergeStrategy[MergeStrategy["Default"] = 0] = "Default";
// Error out if 2 files have the same path. It is useful to have a different value than
// Default in this case as the tooling Default might differ.
MergeStrategy[MergeStrategy["Error"] = 1] = "Error";
// Only content conflicts are overwritten.
MergeStrategy[MergeStrategy["ContentOnly"] = 2] = "ContentOnly";
// Overwrite everything with the latest change.
MergeStrategy[MergeStrategy["Overwrite"] = 14] = "Overwrite";
})(MergeStrategy || (exports.MergeStrategy = MergeStrategy = {}));
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
exports.FileVisitorCancelToken = Symbol();
exports.TreeSymbol = (function () {
const globalSymbol = (typeof window == 'object' && window.window === window && window.Symbol) ||
(typeof self == 'object' && self.self === self && self.Symbol) ||
(typeof global == 'object' && global.global === global && global.Symbol);
if (!globalSymbol) {
return Symbol('schematic-tree');
}
if (!globalSymbol.schematicTree) {
globalSymbol.schematicTree = Symbol('schematic-tree');
}
return globalSymbol.schematicTree;
})();
// eslint-disable-next-line @typescript-eslint/no-namespace
var Tree;
(function (Tree) {
function isTree(maybeTree) {
return exports.TreeSymbol in maybeTree;
}
Tree.isTree = isTree;
})(Tree || (Tree = {}));

View file

@ -0,0 +1,44 @@
/**
* @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.io/license
*/
import { BaseException, JsonValue, Path, PathFragment } from '@angular-devkit/core';
import { Action } from './action';
import { DirEntry, MergeStrategy, Tree, UpdateRecorder } from './interface';
export declare class CannotCreateFileException extends BaseException {
constructor(path: string);
}
export declare class NullTreeDirEntry implements DirEntry {
readonly path: Path;
get parent(): DirEntry | null;
constructor(path: Path);
readonly subdirs: PathFragment[];
readonly subfiles: PathFragment[];
dir(name: PathFragment): DirEntry;
file(_name: PathFragment): null;
visit(): void;
}
export declare class NullTree implements Tree {
branch(): Tree;
merge(_other: Tree, _strategy?: MergeStrategy): void;
readonly root: DirEntry;
exists(_path: string): boolean;
read(_path: string): null;
readText(path: string): string;
readJson(path: string): JsonValue;
get(_path: string): null;
getDir(path: string): NullTreeDirEntry;
visit(): void;
beginUpdate(path: string): never;
commitUpdate(record: UpdateRecorder): never;
copy(path: string, _to: string): never;
delete(path: string): never;
create(path: string, _content: Buffer | string): never;
rename(path: string, _to: string): never;
overwrite(path: string, _content: Buffer | string): never;
apply(_action: Action, _strategy?: MergeStrategy): void;
get actions(): Action[];
}

View file

@ -0,0 +1,97 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NullTree = exports.NullTreeDirEntry = exports.CannotCreateFileException = void 0;
const core_1 = require("@angular-devkit/core");
const exception_1 = require("../exception/exception");
const interface_1 = require("./interface");
const recorder_1 = require("./recorder");
class CannotCreateFileException extends core_1.BaseException {
constructor(path) {
super(`Cannot create file "${path}".`);
}
}
exports.CannotCreateFileException = CannotCreateFileException;
class NullTreeDirEntry {
path;
get parent() {
return this.path == '/' ? null : new NullTreeDirEntry((0, core_1.dirname)(this.path));
}
constructor(path) {
this.path = path;
}
subdirs = [];
subfiles = [];
dir(name) {
return new NullTreeDirEntry((0, core_1.join)(this.path, name));
}
file(_name) {
return null;
}
visit() { }
}
exports.NullTreeDirEntry = NullTreeDirEntry;
class NullTree {
[interface_1.TreeSymbol]() {
return this;
}
branch() {
return new NullTree();
}
merge(_other, _strategy) { }
root = new NullTreeDirEntry((0, core_1.normalize)('/'));
// Simple readonly file system operations.
exists(_path) {
return false;
}
read(_path) {
return null;
}
readText(path) {
throw new exception_1.FileDoesNotExistException(path);
}
readJson(path) {
throw new exception_1.FileDoesNotExistException(path);
}
get(_path) {
return null;
}
getDir(path) {
return new NullTreeDirEntry((0, core_1.normalize)('/' + path));
}
visit() { }
// Change content of host files.
beginUpdate(path) {
throw new exception_1.FileDoesNotExistException(path);
}
commitUpdate(record) {
throw new exception_1.FileDoesNotExistException(record instanceof recorder_1.UpdateRecorderBase ? record.path : '<unknown>');
}
// Change structure of the host.
copy(path, _to) {
throw new exception_1.FileDoesNotExistException(path);
}
delete(path) {
throw new exception_1.FileDoesNotExistException(path);
}
create(path, _content) {
throw new CannotCreateFileException(path);
}
rename(path, _to) {
throw new exception_1.FileDoesNotExistException(path);
}
overwrite(path, _content) {
throw new exception_1.FileDoesNotExistException(path);
}
apply(_action, _strategy) { }
get actions() {
return [];
}
}
exports.NullTree = NullTree;

View file

@ -0,0 +1,28 @@
/**
* @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.io/license
*/
import { UpdateBufferBase } from '../utility/update-buffer';
import { FileEntry, UpdateRecorder } from './interface';
export declare class UpdateRecorderBase implements UpdateRecorder {
protected _path: string;
protected _original: Buffer;
protected _content: UpdateBufferBase;
constructor(entry: FileEntry);
static createFromFileEntry(entry: FileEntry): UpdateRecorderBase;
get path(): string;
insertLeft(index: number, content: Buffer | string): UpdateRecorder;
insertRight(index: number, content: Buffer | string): UpdateRecorder;
remove(index: number, length: number): UpdateRecorder;
apply(content: Buffer): Buffer;
}
export declare class UpdateRecorderBom extends UpdateRecorderBase {
private _delta;
constructor(entry: FileEntry, _delta?: number);
insertLeft(index: number, content: Buffer | string): UpdateRecorder;
insertRight(index: number, content: Buffer | string): UpdateRecorder;
remove(index: number, length: number): UpdateRecorder;
}

View file

@ -0,0 +1,78 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.UpdateRecorderBom = exports.UpdateRecorderBase = void 0;
const exception_1 = require("../exception/exception");
const update_buffer_1 = require("../utility/update-buffer");
class UpdateRecorderBase {
_path;
_original;
_content;
constructor(entry) {
this._original = Buffer.from(entry.content);
this._content = update_buffer_1.UpdateBufferBase.create(entry.path, entry.content);
this._path = entry.path;
}
static createFromFileEntry(entry) {
const c0 = entry.content.byteLength > 0 && entry.content.readUInt8(0);
const c1 = entry.content.byteLength > 1 && entry.content.readUInt8(1);
const c2 = entry.content.byteLength > 2 && entry.content.readUInt8(2);
// Check if we're BOM.
if (c0 == 0xef && c1 == 0xbb && c2 == 0xbf) {
return new UpdateRecorderBom(entry);
}
else if (c0 === 0xff && c1 == 0xfe) {
return new UpdateRecorderBom(entry);
}
else if (c0 === 0xfe && c1 == 0xff) {
return new UpdateRecorderBom(entry);
}
return new UpdateRecorderBase(entry);
}
get path() {
return this._path;
}
// These just record changes.
insertLeft(index, content) {
this._content.insertLeft(index, typeof content == 'string' ? Buffer.from(content) : content);
return this;
}
insertRight(index, content) {
this._content.insertRight(index, typeof content == 'string' ? Buffer.from(content) : content);
return this;
}
remove(index, length) {
this._content.remove(index, length);
return this;
}
apply(content) {
if (!content.equals(this._content.original)) {
throw new exception_1.ContentHasMutatedException(this.path);
}
return this._content.generate();
}
}
exports.UpdateRecorderBase = UpdateRecorderBase;
class UpdateRecorderBom extends UpdateRecorderBase {
_delta;
constructor(entry, _delta = 1) {
super(entry);
this._delta = _delta;
}
insertLeft(index, content) {
return super.insertLeft(index + this._delta, content);
}
insertRight(index, content) {
return super.insertRight(index + this._delta, content);
}
remove(index, length) {
return super.remove(index + this._delta, length);
}
}
exports.UpdateRecorderBom = UpdateRecorderBom;

View file

@ -0,0 +1,48 @@
/**
* @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.io/license
*/
import { JsonValue, Path, PathFragment } from '@angular-devkit/core';
import { Action } from './action';
import { DirEntry, FileEntry, FileVisitor, MergeStrategy, Tree, UpdateRecorder } from './interface';
declare class ScopedDirEntry implements DirEntry {
private _base;
readonly scope: Path;
constructor(_base: DirEntry, scope: Path);
get parent(): DirEntry | null;
get path(): Path;
get subdirs(): PathFragment[];
get subfiles(): PathFragment[];
dir(name: PathFragment): DirEntry;
file(name: PathFragment): FileEntry | null;
visit(visitor: FileVisitor): void;
}
export declare class ScopedTree implements Tree {
private _base;
readonly _root: ScopedDirEntry;
constructor(_base: Tree, scope: string);
get root(): DirEntry;
branch(): Tree;
merge(other: Tree, strategy?: MergeStrategy): void;
read(path: string): Buffer | null;
readText(path: string): string;
readJson(path: string): JsonValue;
exists(path: string): boolean;
get(path: string): FileEntry | null;
getDir(path: string): DirEntry;
visit(visitor: FileVisitor): void;
overwrite(path: string, content: Buffer | string): void;
beginUpdate(path: string): UpdateRecorder;
commitUpdate(record: UpdateRecorder): void;
create(path: string, content: Buffer | string): void;
delete(path: string): void;
rename(from: string, to: string): void;
apply(action: Action, strategy?: MergeStrategy): void;
get actions(): Action[];
private _fullPath;
private _fullPathAction;
}
export {};

View file

@ -0,0 +1,181 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScopedTree = void 0;
const core_1 = require("@angular-devkit/core");
const delegate_1 = require("./delegate");
const interface_1 = require("./interface");
class ScopedFileEntry {
_base;
scope;
constructor(_base, scope) {
this._base = _base;
this.scope = scope;
}
get path() {
return (0, core_1.join)(core_1.NormalizedRoot, (0, core_1.relative)(this.scope, this._base.path));
}
get content() {
return this._base.content;
}
}
class ScopedDirEntry {
_base;
scope;
constructor(_base, scope) {
this._base = _base;
this.scope = scope;
}
get parent() {
if (!this._base.parent || this._base.path == this.scope) {
return null;
}
return new ScopedDirEntry(this._base.parent, this.scope);
}
get path() {
return (0, core_1.join)(core_1.NormalizedRoot, (0, core_1.relative)(this.scope, this._base.path));
}
get subdirs() {
return this._base.subdirs;
}
get subfiles() {
return this._base.subfiles;
}
dir(name) {
const entry = this._base.dir(name);
return entry && new ScopedDirEntry(entry, this.scope);
}
file(name) {
const entry = this._base.file(name);
return entry && new ScopedFileEntry(entry, this.scope);
}
visit(visitor) {
return this._base.visit((path, entry) => {
visitor((0, core_1.join)(core_1.NormalizedRoot, (0, core_1.relative)(this.scope, path)), entry && new ScopedFileEntry(entry, this.scope));
});
}
}
class ScopedTree {
_base;
_root;
constructor(_base, scope) {
this._base = _base;
const normalizedScope = (0, core_1.normalize)('/' + scope);
this._root = new ScopedDirEntry(this._base.getDir(normalizedScope), normalizedScope);
}
get root() {
return this._root;
}
branch() {
return new ScopedTree(this._base.branch(), this._root.scope);
}
merge(other, strategy) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
const delegate = new (class extends delegate_1.DelegateTree {
get actions() {
return other.actions.map((action) => self._fullPathAction(action));
}
})(other);
this._base.merge(delegate, strategy);
}
// Readonly.
read(path) {
return this._base.read(this._fullPath(path));
}
readText(path) {
return this._base.readText(this._fullPath(path));
}
readJson(path) {
return this._base.readJson(this._fullPath(path));
}
exists(path) {
return this._base.exists(this._fullPath(path));
}
get(path) {
const entry = this._base.get(this._fullPath(path));
return entry && new ScopedFileEntry(entry, this._root.scope);
}
getDir(path) {
const entry = this._base.getDir(this._fullPath(path));
return entry && new ScopedDirEntry(entry, this._root.scope);
}
visit(visitor) {
return this._root.visit(visitor);
}
// Change content of host files.
overwrite(path, content) {
return this._base.overwrite(this._fullPath(path), content);
}
beginUpdate(path) {
return this._base.beginUpdate(this._fullPath(path));
}
commitUpdate(record) {
return this._base.commitUpdate(record);
}
// Structural methods.
create(path, content) {
return this._base.create(this._fullPath(path), content);
}
delete(path) {
return this._base.delete(this._fullPath(path));
}
rename(from, to) {
return this._base.rename(this._fullPath(from), this._fullPath(to));
}
apply(action, strategy) {
return this._base.apply(this._fullPathAction(action), strategy);
}
get actions() {
const scopedActions = [];
for (const action of this._base.actions) {
if (!action.path.startsWith(this._root.scope + '/')) {
continue;
}
if (action.kind !== 'r') {
scopedActions.push({
...action,
path: (0, core_1.join)(core_1.NormalizedRoot, (0, core_1.relative)(this._root.scope, action.path)),
});
}
else if (action.to.startsWith(this._root.scope + '/')) {
scopedActions.push({
...action,
path: (0, core_1.join)(core_1.NormalizedRoot, (0, core_1.relative)(this._root.scope, action.path)),
to: (0, core_1.join)(core_1.NormalizedRoot, (0, core_1.relative)(this._root.scope, action.to)),
});
}
}
return scopedActions;
}
[interface_1.TreeSymbol]() {
return this;
}
_fullPath(path) {
return (0, core_1.join)(this._root.scope, (0, core_1.normalize)('/' + path));
}
_fullPathAction(action) {
let fullPathAction;
if (action.kind === 'r') {
fullPathAction = {
...action,
path: this._fullPath(action.path),
to: this._fullPath(action.to),
};
}
else {
fullPathAction = {
...action,
path: this._fullPath(action.path),
};
}
return fullPathAction;
}
}
exports.ScopedTree = ScopedTree;

View file

@ -0,0 +1,13 @@
/**
* @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.io/license
*/
import { HostTree } from './host-tree';
import { FilePredicate, MergeStrategy, Tree } from './interface';
export declare function empty(): HostTree;
export declare function branch(tree: Tree): Tree;
export declare function merge(tree: Tree, other: Tree, strategy?: MergeStrategy): Tree;
export declare function partition(tree: Tree, predicate: FilePredicate<boolean>): [Tree, Tree];

View file

@ -0,0 +1,38 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.partition = exports.merge = exports.branch = exports.empty = void 0;
const exception_1 = require("../exception/exception");
const host_tree_1 = require("./host-tree");
const interface_1 = require("./interface");
function empty() {
return new host_tree_1.HostTree();
}
exports.empty = empty;
function branch(tree) {
return tree.branch();
}
exports.branch = branch;
function merge(tree, other, strategy = interface_1.MergeStrategy.Default) {
tree.merge(other, strategy);
return tree;
}
exports.merge = merge;
function partition(tree, predicate) {
if (tree instanceof host_tree_1.HostTree) {
return [
new host_tree_1.FilterHostTree(tree, predicate),
new host_tree_1.FilterHostTree(tree, (path, entry) => !predicate(path, entry)),
];
}
else {
throw new exception_1.SchematicsException('Tree type is not supported.');
}
}
exports.partition = partition;

View file

@ -0,0 +1,50 @@
/**
* @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.io/license
*/
import { BaseException } from '@angular-devkit/core';
import MagicString from 'magic-string';
export declare class IndexOutOfBoundException extends BaseException {
constructor(index: number, min: number, max?: number);
}
/**
* Base class for an update buffer implementation that allows buffers to be inserted to the _right
* or _left, or deleted, while keeping indices to the original buffer.
*/
export declare abstract class UpdateBufferBase {
protected _originalContent: Buffer;
constructor(_originalContent: Buffer);
abstract get length(): number;
abstract get original(): Buffer;
abstract toString(encoding?: string): string;
abstract generate(): Buffer;
abstract insertLeft(index: number, content: Buffer, assert?: boolean): void;
abstract insertRight(index: number, content: Buffer, assert?: boolean): void;
abstract remove(index: number, length: number): void;
/**
* Creates an UpdateBufferBase instance.
*
* @param contentPath The path of the update buffer instance.
* @param originalContent The original content of the update buffer instance.
* @returns An UpdateBufferBase instance.
*/
static create(contentPath: string, originalContent: Buffer): UpdateBufferBase;
}
/**
* An utility class that allows buffers to be inserted to the _right or _left, or deleted, while
* keeping indices to the original buffer.
*/
export declare class UpdateBuffer extends UpdateBufferBase {
protected _mutatableContent: MagicString;
protected _assertIndex(index: number): void;
get length(): number;
get original(): Buffer;
toString(): string;
generate(): Buffer;
insertLeft(index: number, content: Buffer): void;
insertRight(index: number, content: Buffer): void;
remove(index: number, length: number): void;
}

View file

@ -0,0 +1,90 @@
"use strict";
/**
* @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.io/license
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UpdateBuffer = exports.UpdateBufferBase = exports.IndexOutOfBoundException = void 0;
const core_1 = require("@angular-devkit/core");
const magic_string_1 = __importDefault(require("magic-string"));
const node_util_1 = require("node:util");
class IndexOutOfBoundException extends core_1.BaseException {
constructor(index, min, max = Infinity) {
super(`Index ${index} outside of range [${min}, ${max}].`);
}
}
exports.IndexOutOfBoundException = IndexOutOfBoundException;
/**
* Base class for an update buffer implementation that allows buffers to be inserted to the _right
* or _left, or deleted, while keeping indices to the original buffer.
*/
class UpdateBufferBase {
_originalContent;
constructor(_originalContent) {
this._originalContent = _originalContent;
}
/**
* Creates an UpdateBufferBase instance.
*
* @param contentPath The path of the update buffer instance.
* @param originalContent The original content of the update buffer instance.
* @returns An UpdateBufferBase instance.
*/
static create(contentPath, originalContent) {
try {
// We only support utf8 encoding.
new node_util_1.TextDecoder('utf8', { fatal: true }).decode(originalContent);
return new UpdateBuffer(originalContent);
}
catch (e) {
if (e instanceof TypeError) {
throw new Error(`Failed to decode "${contentPath}" as UTF-8 text.`);
}
throw e;
}
}
}
exports.UpdateBufferBase = UpdateBufferBase;
/**
* An utility class that allows buffers to be inserted to the _right or _left, or deleted, while
* keeping indices to the original buffer.
*/
class UpdateBuffer extends UpdateBufferBase {
_mutatableContent = new magic_string_1.default(this._originalContent.toString());
_assertIndex(index) {
if (index < 0 || index > this._originalContent.length) {
throw new IndexOutOfBoundException(index, 0, this._originalContent.length);
}
}
get length() {
return this._mutatableContent.length();
}
get original() {
return this._originalContent;
}
toString() {
return this._mutatableContent.toString();
}
generate() {
return Buffer.from(this.toString());
}
insertLeft(index, content) {
this._assertIndex(index);
this._mutatableContent.appendLeft(index, content.toString());
}
insertRight(index, content) {
this._assertIndex(index);
this._mutatableContent.appendRight(index, content.toString());
}
remove(index, length) {
this._assertIndex(index);
this._mutatableContent.remove(index, index + length);
}
}
exports.UpdateBuffer = UpdateBuffer;

View file

@ -0,0 +1,51 @@
/**
* @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.io/license
*/
import { schema, virtualFs } from '@angular-devkit/core';
import { Observable, Subject } from 'rxjs';
import { Engine, EngineHost } from '../engine';
import { DryRunEvent } from '../sink/dryrun';
import { Sink } from '../sink/sink';
import { LifeCycleEvent, RequiredWorkflowExecutionContext, Workflow, WorkflowExecutionContext } from './interface';
export interface BaseWorkflowOptions {
host: virtualFs.Host;
engineHost: EngineHost<{}, {}>;
registry?: schema.CoreSchemaRegistry;
force?: boolean;
dryRun?: boolean;
}
/**
* Base class for workflows. Even without abstract methods, this class should not be used without
* surrounding some initialization for the registry and host. This class only adds life cycle and
* dryrun/force support. You need to provide any registry and task executors that you need to
* support.
* See {@see NodeWorkflow} implementation for how to make a specialized subclass of this.
* TODO: add default set of CoreSchemaRegistry transforms. Once the job refactor is done, use that
* as the support for tasks.
*
* @public
*/
export declare abstract class BaseWorkflow implements Workflow {
protected _engine: Engine<{}, {}>;
protected _engineHost: EngineHost<{}, {}>;
protected _registry: schema.CoreSchemaRegistry;
protected _host: virtualFs.Host;
protected _reporter: Subject<DryRunEvent>;
protected _lifeCycle: Subject<LifeCycleEvent>;
protected _context: WorkflowExecutionContext[];
protected _force: boolean;
protected _dryRun: boolean;
constructor(options: BaseWorkflowOptions);
get context(): Readonly<WorkflowExecutionContext>;
get engine(): Engine<{}, {}>;
get engineHost(): EngineHost<{}, {}>;
get registry(): schema.SchemaRegistry;
get reporter(): Observable<DryRunEvent>;
get lifeCycle(): Observable<LifeCycleEvent>;
protected _createSinks(): Sink[];
execute(options: Partial<WorkflowExecutionContext> & RequiredWorkflowExecutionContext): Observable<void>;
}

View file

@ -0,0 +1,144 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseWorkflow = void 0;
const core_1 = require("@angular-devkit/core");
const rxjs_1 = require("rxjs");
const engine_1 = require("../engine");
const exception_1 = require("../exception/exception");
const formats_1 = require("../formats");
const dryrun_1 = require("../sink/dryrun");
const host_1 = require("../sink/host");
const host_tree_1 = require("../tree/host-tree");
/**
* Base class for workflows. Even without abstract methods, this class should not be used without
* surrounding some initialization for the registry and host. This class only adds life cycle and
* dryrun/force support. You need to provide any registry and task executors that you need to
* support.
* See {@see NodeWorkflow} implementation for how to make a specialized subclass of this.
* TODO: add default set of CoreSchemaRegistry transforms. Once the job refactor is done, use that
* as the support for tasks.
*
* @public
*/
class BaseWorkflow {
_engine;
_engineHost;
_registry;
_host;
_reporter = new rxjs_1.Subject();
_lifeCycle = new rxjs_1.Subject();
_context;
_force;
_dryRun;
constructor(options) {
this._host = options.host;
this._engineHost = options.engineHost;
if (options.registry) {
this._registry = options.registry;
}
else {
this._registry = new core_1.schema.CoreSchemaRegistry(formats_1.standardFormats);
this._registry.addPostTransform(core_1.schema.transforms.addUndefinedDefaults);
}
this._engine = new engine_1.SchematicEngine(this._engineHost, this);
this._context = [];
this._force = options.force || false;
this._dryRun = options.dryRun || false;
}
get context() {
const maybeContext = this._context[this._context.length - 1];
if (!maybeContext) {
throw new Error('Cannot get context when workflow is not executing...');
}
return maybeContext;
}
get engine() {
return this._engine;
}
get engineHost() {
return this._engineHost;
}
get registry() {
return this._registry;
}
get reporter() {
return this._reporter.asObservable();
}
get lifeCycle() {
return this._lifeCycle.asObservable();
}
_createSinks() {
let error = false;
const dryRunSink = new dryrun_1.DryRunSink(this._host, this._force);
const dryRunSubscriber = dryRunSink.reporter.subscribe((event) => {
this._reporter.next(event);
error = error || event.kind == 'error';
});
// We need two sinks if we want to output what will happen, and actually do the work.
return [
dryRunSink,
// Add a custom sink that clean ourselves and throws an error if an error happened.
{
commit() {
dryRunSubscriber.unsubscribe();
if (error) {
return (0, rxjs_1.throwError)(new exception_1.UnsuccessfulWorkflowExecution());
}
return (0, rxjs_1.of)();
},
},
// Only add a HostSink if this is not a dryRun.
...(!this._dryRun ? [new host_1.HostSink(this._host, this._force)] : []),
];
}
execute(options) {
const parentContext = this._context[this._context.length - 1];
if (!parentContext) {
this._lifeCycle.next({ kind: 'start' });
}
/** Create the collection and the schematic. */
const collection = this._engine.createCollection(options.collection);
// Only allow private schematics if called from the same collection.
const allowPrivate = options.allowPrivate || (parentContext && parentContext.collection === options.collection);
const schematic = collection.createSchematic(options.schematic, allowPrivate);
const sinks = this._createSinks();
this._lifeCycle.next({ kind: 'workflow-start' });
const context = {
...options,
debug: options.debug || false,
logger: options.logger || (parentContext && parentContext.logger) || new core_1.logging.NullLogger(),
parentContext,
};
this._context.push(context);
return schematic
.call(options.options, (0, rxjs_1.of)(new host_tree_1.HostTree(this._host)), { logger: context.logger })
.pipe((0, rxjs_1.concatMap)((tree) => {
// Process all sinks.
return (0, rxjs_1.concat)((0, rxjs_1.from)(sinks).pipe((0, rxjs_1.concatMap)((sink) => sink.commit(tree)), (0, rxjs_1.ignoreElements)()), (0, rxjs_1.of)(tree));
}), (0, rxjs_1.concatMap)(() => {
if (this._dryRun) {
return rxjs_1.EMPTY;
}
this._lifeCycle.next({ kind: 'post-tasks-start' });
return this._engine
.executePostTasks()
.pipe((0, rxjs_1.tap)({ complete: () => this._lifeCycle.next({ kind: 'post-tasks-end' }) }), (0, rxjs_1.defaultIfEmpty)(undefined), (0, rxjs_1.last)());
}), (0, rxjs_1.tap)({
complete: () => {
this._lifeCycle.next({ kind: 'workflow-end' });
this._context.pop();
if (this._context.length == 0) {
this._lifeCycle.next({ kind: 'end' });
}
},
}));
}
}
exports.BaseWorkflow = BaseWorkflow;

View file

@ -0,0 +1,9 @@
/**
* @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.io/license
*/
export * from './base';
export * from './interface';

View file

@ -0,0 +1,25 @@
"use strict";
/**
* @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.io/license
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./base"), exports);
__exportStar(require("./interface"), exports);

View file

@ -0,0 +1,27 @@
/**
* @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.io/license
*/
import { logging } from '@angular-devkit/core';
import { Observable } from 'rxjs';
export interface RequiredWorkflowExecutionContext {
collection: string;
schematic: string;
options: object;
}
export interface WorkflowExecutionContext extends RequiredWorkflowExecutionContext {
debug: boolean;
logger: logging.Logger;
parentContext?: Readonly<WorkflowExecutionContext>;
allowPrivate?: boolean;
}
export interface LifeCycleEvent {
kind: 'start' | 'end' | 'workflow-start' | 'workflow-end' | 'post-tasks-start' | 'post-tasks-end';
}
export interface Workflow {
readonly context: Readonly<WorkflowExecutionContext>;
execute(options: Partial<WorkflowExecutionContext> & RequiredWorkflowExecutionContext): Observable<void>;
}

View file

@ -0,0 +1,9 @@
"use strict";
/**
* @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.io/license
*/
Object.defineProperty(exports, "__esModule", { value: true });