vue-cli icon indicating copy to clipboard operation
vue-cli copied to clipboard

Add the ability to reorder plugin execution

Open hl037 opened this issue 4 years ago • 3 comments

What problem does this feature solve?

Currently, plugins are executed in the order of the package.json dependency list. Since these dependencies are automatically alphabetically sorted by yarn install, One plugin can suddenly break when renamed if it expect some other plugin to be executed before ( see https://stackoverflow.com/questions/65977135/how-to-change-chainwebpack-call-order-with-vue-js ) ...This would also bring some determism to when each plugin is called

What does the proposed API look like?

An API could for example bring some kind of dependency graph across the plugins, like this :

First, the main export would be either a callback as it is currently, either an object. if it is an object, it would look like this :

{
  before? : array<string>,
  after? : array<string>,
  name? : string,
  callback : (api, options) => void),
  metaPlugin? : boolean
}

metaPlugin defaults to False. Indicates if callback will contains only calls to api.addPlugin() (defined below). If it is set, before, after and name should be left undefined/null/empty.

callback is equivalent to the current exported function.

If metaPlugin is not set :

name, if set is the name of group this plugin is part of. if not set, it defaults to the package name.

before and after are the requirement lists of the plugin in the dependency graph. (other plugin's name)

Then an addPlugin()

api.addPlugin({
  before? : array<string>,
  after? : array<string>,
  name? : string,
  callback : (api, options) => void),
  metaPlugin? : boolean
})

The fields have the same semantic as the exported object.

The plugin would be executed like this :

First, gather all (meta)plugin. Execute all meta-plugins "recursively" (a meta plugin can add another meta-plugin in its callback).

Then, solve the dep-graph and execute the remaining plugins. If not possible (cyclic dependencies), raise an error.

Example :

const dts = require('dts-bundle');
const path = require('path');

module.exports = {
  metaPlugin : true
  callback = (api, options) => {
    api.addPlugin({
      after : ["ts-loader"],
      name : "ts-bundler",
      callback : (api, options) => {

        api.chainWebpack(config => {
          //Disable thread-loader, cache-loader
          const tsRule = config.module.rule('ts').test(/\.ts$/);
          const tsxRule = config.module.rule('tsx').test(/\.tsx$/);

          debugger
          tsRule.uses.delete('cache-loader');
          tsxRule.uses.delete('cache-loader');
          tsRule.uses.delete('thread-loader');
          tsxRule.uses.delete('thread-loader');

          //Enable the generating of declaration files 
          tsRule
            .use('ts-loader')
            .loader('ts-loader')
            .tap(opts => {
              debugger
              opts.compilerOptions = { declaration: true };
              opts.transpileOnly = false;
              opts.happyPackMode = false;
              return opts;
            });
        });

        //Bundle command
        api.registerCommand('bundle-dts', {
          description: 'Bundle the generated declaration files to a single file',
          usage: 'vue-cli-service bundle-dts [options]',
        }, async (args) => {
          const config = api.resolveWebpackConfig();

          const baseDir = config.output.path;
          const entry = path.parse(config.entry.app[0]);
          const main = path.resolve(baseDir, entry.dir, entry.name   '.d.ts');
          const name = require(api.resolve('package.json')).name;

          delete args['_'];

          if (args.main) {
            const main = path.parse(args.main);
            args.main = path.resolve(baseDir, main.dir, main.name   '.d.ts');
          }

          dts.bundle({
            ...{ baseDir, main, name },
            ...args
          });
        });
      }
    });
  }
}

hl037 avatar Jan 31 '21 10:01 hl037

I prefer something like below

module.exports = (api, options) => {
//...
}
module.exports.before = ['@vue/cli-plugin-typescript']

fangbinwei avatar Jan 31 '21 11:01 fangbinwei

I like it, it would indeed be better

hl037 avatar Jan 31 '21 11:01 hl037

topologicalSorting implementation of backward sorting, can't put my plugin on first

leookun avatar Feb 04 '24 09:02 leookun