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/[email protected]
├── @angular/[email protected]
├── @schematics/[email protected]
├── [email protected]
├── [email protected]
└── [email protected]

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

2
  • 1
    UPDATE: I ran an npm cache clean --force, deleted the local repo, re-cloned, and everything is now working fine. If anyone has insight though, please do leave a comment/answer. Thank you Commented Oct 4, 2021 at 17:03
  • Which version of NPM are you using? Commented Dec 11, 2021 at 5:17

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.