oclif / core

Node.js Open CLI Framework. Built by Salesforce.

Home Page:https://oclif.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Specify different oclif options for dev and run

alexpchin opened this issue · comments

Due to the way my typescript is compiling (due to requiring a parent package) my code in:

/src/commands

Compiles to:

/dist/bunk-cli/src/commands

This means that when setting up the oclif options, oclif can't work out where to find the commands.

"oclif:dev": {
    "bin": "bunk-cli",
    "commands": "./src/commands",
    "hooks": {
      "init-config": "./src/hooks/custom/init-config"
    }
  },
  "oclif:dist": {
    "bin": "bunk-cli",
    "commands": "./dist/bunk-cli/src/commands",
    "hooks": {
      "init-config": "./dist/bunk-cli/src/hooks/custom/init-config"
    }
  },

One works for dev and one for run but they aren't interchangeable. Is is possible to fix or to specify different options via the loadOptions of execute?

The issue I am facing throws an error from ts-path:

    debug(`No source file found. Returning default path ${orig}`);
    if (!(0, util_1.isProd)())
        (0, errors_1.memoizedWarn)(`Could not find source for ${orig} based on tsconfig. Defaulting to compiled source.`);
    return orig;

i.e. Warning: Could not find source for bunk-monorepo/bunk-cli/src/commands based on tsconfig. Defaulting to compiled source.

@alexpchin The compiled source path is determined based on the outDir and rootDir (or rootDirs or baseUrl) - which happens here. So maybe there's something that you can tweak there to make it find the right path?

Hi @mdonnalley the reason I'm having the issue is that I'm using pnpm and I'm requiring another package without setting rootDir, just setting baseUrl. This means each package is added to the lib folder.

I could possibly look at tweaking this by looking at typescript references (I believe) but this may/may not work for us.

Is there no way to supply an alternative config just for use with ts-node as it won't be looking in the lib folder? If I change to /src/commands everything works with ts-node which obviously breaks then when using run?

Let me make sure I understand:

You want to use ./src/commands when running bin/dev.js and you want to use ./dist/bunk-cli/src/commands when running bin/run.js? And the reason is that tsPath can't correctly resolve ./dist/bunk-cli/src/commands back to ./src/commands?

Is there no way to supply an alternative config just for use with ts-node as it won't be looking in the lib folder?

No, I can't think of anything that would allow you to do this without some code changes.

Probably the easiest code change that could be made is to have an env var that overrides any reference to this.pjson.oclif.commands in src/config/plugin.ts

Usage would be something like:

OCLIF_COMMANDS_OVERRIDE=./src/commands bin/dev.js

Would be happy to accept a PR if you would like to make that change.

If you don't want that, I have a couple of ideas you could try on your side:

  • Precompile code prior to using bin/dev.js (I realize that this defeats the purpose of bin/dev.js)
  • Have bin/dev.js modify the package.json prior to executing and then restoring it after execution completes

Neither of those are great but you could give them a try.

@mdonnalley That's exactly the issue. A couple of ideas, could we enable passing of options to execute via loadOptions? Or specify a .oclifrc file?

this.OCLIF_CONFIG = process.env.OCLIFRC ?? 'package.json';

this.pjson = await readJson(join(root, this.OCLIF_CONFIG));

It might allow for more configuration without having to set separate options as environment variables?

Basically, the problem is that ts-node and tsc output files differently when not using typescript's rootDir in a monorepo (TypeScript determines one parent folder containing all your source files. This folder is set as [rootDir] in tsconfig.json or calculated automatically by the compiler).

So I think it would be useful to be able to set:

  • .oclifrc.dev.json for dev (ts-node)
  • .oclifrc.json for both build script and run (tsc).

Another idea, you could also possibly add the bunk-cli/src as a relativeOutDir option? I.e. when using ts-node and an relativeOutDir option is provided, remove it from the commands and hooks in package.json? To make /dist/bunk-cli/src/commands become /dist/commands? /outDir/(relativeOutDir?)/commands

I don't think that we're likely to add support for an rc file. As it is, we're just too dependent on the package.json (for things other than the oclif section) and I don't want to add the complexity of another configuration source.

For an unrelated reason, I added a pjson option to the LoadOptions that can be passed to execute in #945

You'd have to read the package.json, modify oclif.commands and then pass that into execute. Something like this:

const pjson = JSON.parse(await readFile('package.json', 'utf8'))
pjson.oclif.commands = './src/commands'
await execute({
  loadOptions: {
    pjson,
    root: __dirname,
  },
})

Would that work for you?

If you want, you can test it out yourself using @oclif/core@3.20.1-dev.1

Hi @mdonnalley Thanks, that works nicely! Amazing. Just for reference this is my full dev.js

#!/usr/bin/env -S node --import ts-node/esm --no-warnings=ExperimentalWarning
// eslint-disable-next-line unicorn/prefer-top-level-await
(async () => {
  const tsConfig = await import('../tsconfig.json');
  const tsConfigPaths = await import('tsconfig-paths');

  tsConfigPaths.register({
    baseUrl: './',
    paths: tsConfig.compilerOptions.paths,
  });
  /**
   * Due to typescript building into dist in a non-flat way,
   * i.e. tsc is assuming the rootDir to be the root of the momonrepo
   * because we're requiring files from other packages, i.e. bunk-models
   * then we have to load a different location for the oclif commands
   * to work when loading with ts-node which is on-the-fly.
   */
  const { readFile } = await import('fs/promises');
  const path = await import('path');
  const pjsonPath = path.join(__dirname, '..', 'package.json');
  const data = await readFile(pjsonPath, 'utf8');
  const pjson = JSON.parse(data);
  const commandsPath = './src/commands';
  const hooksPath = './src/hooks/custom/init-config';
  pjson.oclif = {
    bin: 'bunk-cli',
    commands: commandsPath,
    hooks: {
      'init-config': hooksPath,
    },
  };
  const { execute, settings } = await import('@oclif/core');
  // settings.performanceEnabled = true;
  const root = path.join(__dirname, '..');
  await execute({
    development: true,
    loadOptions: {
      pjson,
      root,
    },
  });
})();