I feel like my custom schematics is cached somewhere and is running an old version. Before the weekend I had published my schematic to npm, and had installed globally. I made some updates to my index.ts, and no matter what I do, the old schematic code is still running when i execute the command 'schematics .:ng-new-gci (when in the root schematics project). It runs the schematic just fine, prompts the user, but I have OLD console.logs in there that are still running and are 100% deleted from my index.ts
The first thing I realized is that I had globally installed my schematic with npm after publishing for testing purposes. I went ahead and uninstalled it globally by type 'npm uninstall -g gci-angular-base'
Can now verify that it is no longer installed globally with npm list -g, here is the output
/Users/cwilson3/.nvm/versions/node/v14.17.6/lib
├── @angular-devkit/schematics-cli@12.2.7
├── @angular/cli@12.2.7
├── @schematics/angular@12.2.7
├── json-server@0.16.3
├── nodemon@2.0.12
└── npm@7.24.1
I also deleted my node_modules, package-lock, and re-installed everything in my schematic project. Here is the current package.json (it definitely does not have a reference to the global npm package)
{
"name": "gci-angular-base",
"version": "1.0.0",
"description": "A schematic to scaffold GCI Angular projects",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc -p tsconfig.json",
"build:watch": "tsc -p tsconfig.json --watch",
"prepublish": "tsc"
},
"keywords": [
"gci",
"schematic"
],
"author": "Chris Wilson",
"license": "MIT",
"schematics": "./src/collection.json",
"dependencies": {
"@angular-devkit/core": "^12.2.4",
"@angular-devkit/schematics": "^12.2.4",
"@schematics/angular": "^12.2.7",
"typescript": "~4.3.2"
},
"devDependencies": {
"@types/jasmine": "~3.8.0",
"@types/node": "^12.11.1",
"jasmine": "^3.5.0"
}
}
Next I ran an npm cache verify, and it garbage-collected some data and heres the output
Cache verified and compressed (~/.npm/_cacache)
Content verified: 4619 (410086301 bytes)
Content garbage-collected: 1 (4501858 bytes)
Index entries: 6612
Finished in 13.802s
Here is my index.ts, (it keeps referencing a console.log that is NOT in my code, and its not adding an updated entry that I am writing to angular.json..
// import * as ts from "typescript";
// import * as fs from "fs";
import { logging, strings } from "@angular-devkit/core";
import {
apply,
chain,
externalSchematic,
MergeStrategy,
mergeWith,
Rule,
SchematicContext,
Tree,
url,
template,
move,
forEach,
FileEntry,
SchematicsException
} from "@angular-devkit/schematics";
import { NodePackageInstallTask } from "@angular-devkit/schematics/tasks";
import { Schema } from "./models/schema";
import {
DependencyNames,
DependencyTypesByName,
} from "./models/dependency-types.enum";
// import { getSourceNodes } from '@schematics/angular/utility/ast-utils';
const latestVersions = require("./util/latest-versions.json");
const tsconfigFile = require("./util/tsconfig-override.json");
// You can export this functino as default. You can also have more than one rule factory
// per file. However, exporting as default allows you to not specify the init function name inside
// your collection.json file. In our case we did point to this function in the collection.json
// "factory" property
export function gciAngularBase(_options: Schema): Rule {
const { projectName, addRouting, dependencies, pwa } = _options;
return (tree: Tree, _context: SchematicContext) => {
const logger = _context.logger;
// Merge all the files in our 'files' folder into the virtual structure tree
// Note - strings util allows us to interpolate values into our files
const templateSource = apply(url("./files"), [
template({ ..._options, ...strings }),
forEach((fileEntry: FileEntry) => {
if (tree.exists(fileEntry.path)) {
return null;
}
return fileEntry;
}),
// Move everything into our base project folder
move(`./`),
]);
const merged = mergeWith(templateSource, MergeStrategy.Overwrite);
// Meat and potatoes - this is the rule chain returned by this factory function
const rule = chain([
_generateRepo({ projectName, addRouting }),
merged,
_updatePackageJson({ projectName, dependencies, pwa }, logger, _context),
_setTreeStructure(_options, _context),
]);
return rule(tree, _context) as Rule;
};
}
// This is the first rule in our rule chain, its Angular's ng new schematic
function _generateRepo(_options: Partial<Schema>): Rule {
return externalSchematic("@schematics/angular", "ng-new", {
name: _options.projectName,
directory: _options.projectName,
routing: _options.addRouting,
style: "scss",
inlineStyle: false,
inlineTemplate: false,
// Sadly we have to hard-code all versions, which even Angular does in their schematics
// code in their repo
version: latestVersions["angular-cli"],
});
}
// This function handles all dependencies, if the user selected any from the menu
function _updatePackageJson(
options: Partial<Schema>,
log: logging.LoggerApi,
_context: SchematicContext
): Rule {
const { projectName, dependencies, pwa } = options;
const path = `${projectName}`;
const pkgJsonPath = `${path}/package.json`;
return (tree: Tree): Tree => {
if (!!pwa) {
dependencies?.push("pwa");
}
if (dependencies && dependencies?.length > 0) {
dependencies.forEach((name: string) => {
addDependencyToPkgJson(
tree,
DependencyNames[name],
DependencyTypesByName[name],
latestVersions[name],
pkgJsonPath
);
log.info(`Added ${name} dependency`);
});
}
// This auto runs an npm install
_context.addTask(new NodePackageInstallTask());
const sourcePkgJson = tree.read(pkgJsonPath)!.toString("utf-8");
const json = JSON.parse(sourcePkgJson);
// Add a few standard scripts to package.json
json.scripts = {
...json.scripts,
"build-prod":
"node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng build --prod --output-path 'dist/'",
build: "ng build",
start: "ng serve",
"test-headless":
"ng test --watch=false --browsers ChromeHeadlessNoSandbox --code-coverage",
};
// Check for husky and prettier and add hooks
if (dependencies?.includes("husky")) {
if (dependencies?.includes("prettier")) {
json.husky = {
hooks: {
"pre-commit": "npm run prettier",
"pre-push": "npm run test-headless",
},
};
} else {
json.husky = {
hooks: {
"pre-push": "npm run test-headless",
},
};
}
}
// Write changes to the file and move on
tree.overwrite(pkgJsonPath, JSON.stringify(json, null, 2));
return tree;
};
}
function addDependencyToPkgJson(
tree: Tree,
pkgName: string,
type: string,
version: string,
path: string
): Tree {
if (tree.exists(path)) {
const sourcePkgJson = tree.read(path)!.toString("utf-8");
const json = JSON.parse(sourcePkgJson);
if (!json.dependencies) {
json.dependencies = {};
}
if (!json.devDependencies) {
json.devDependencies = {};
}
if (type === "Default" && !json.dependencies[pkgName]) {
json.dependencies[pkgName] = version;
}
if (type === "Dev" && !json.dependencies[pkgName]) {
json.devDependencies[pkgName] = version;
}
tree.overwrite(path, JSON.stringify(json, null, 2));
}
return tree;
}
// Here is where we will start modifying existing files, services, etc
function _setTreeStructure(
options: Partial<Schema>,
_context: SchematicContext
): Rule {
return (tree: Tree) => {
let path = `./${options.projectName}/tsconfig.json`;
if (tree.exists(path)) {
tree.overwrite(path, JSON.stringify(tsconfigFile, null, 2));
} else {
throw new SchematicsException("Couldnt locate tsconfig.json");
}
path = `./${options.projectName}/angular.json`;
if (!tree.exists(path)) {
throw new SchematicsException("Couldnt locate angular.json");
}
console.log("FOUND ANGULAR.JSON");
const angularJsonSrc = tree.read(path)!.toString("utf-8");
let json = JSON.parse(angularJsonSrc);
const newValue = [
"src/favicon.ico",
"src/fonts",
"src/assets",
"src/app-config",
"src/TEST"
];
Object.keys(json).forEach((key: string) => {
if ((Array.isArray(json[key]) && key === 'assets') || typeof json[key] === 'object') {
return recurseJson(json[key], newValue, 'assets');
} else {
console.log('NOT ARRAY OR Object, skipping!!!');
return;
}
});
// json.projects[`${options.projectName}`].architect.build.options.assets = newValue;
console.log('UPDATED ANGULAR: ', JSON.stringify(json, null, 2));
tree.overwrite(path, JSON.stringify(json, null, 2));
return tree;
};
}
function recurseJson(json: object, newValue: any, searchTerm: string) {
// console.log('CURRENT JSON PASSED: ', JSON.stringify(json, null, 2));
// if (Array.isArray(json)) {
// json = newValue;
// return json;
// } else if (typeof json === 'object') {
// return Object.keys(json).forEach((key: string) => {
// return recurseJson(json[key], newValue, searchTerm);
// })
// } else {
// return json;
// }
// return Object.keys(json).forEach((key: string) => {
// if (Array.isArray(json[key]) && key === searchTerm) {
// json[key] = newValue;
// console.log('FOUND ARRAY WITH: ', json[key]);
// return json;
// } else if (typeof json[key] === 'object') {
// console.log('FOUND OBJECT WITH: ', json[key]);
// return recurseJson(json[key], newValue, searchTerm);
// } else {
// console.log('FOUND NEITHER OBJECT NOR ARRAY: ', json[key]);
// return;
// }
// })
}
Inside the _setTreeStructure function, it is still calling an old console.log that is no longer in my code.......
The one other thing is I also ran 'npm pack', which generated a tarball right into my project, but I have deleted that.
I cannot think of any other way that there is a cache somewhere that is running the old schematic code, can anyone please help? (I have not run an npm cache clean because it seems destructive but if I have to I will)
Thank you, Chris