0

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

Chris Wilson
  • 135
  • 3
  • 16

0 Answers0