Skip to content

Commit

Permalink
Major housekeeping for plugins + configs (consistency + bug fixes)
Browse files Browse the repository at this point in the history
  • Loading branch information
webpro committed Oct 16, 2023
1 parent 589c69a commit 9fd764b
Show file tree
Hide file tree
Showing 90 changed files with 562 additions and 328 deletions.
2 changes: 1 addition & 1 deletion fixtures/plugins/webpack/webpack.config.js
@@ -1,4 +1,4 @@
const getDevelopmentConfig = require('./webpack.dev.js');
const getProductionConfig = require('./webpack.prod.js');

module.exports = (env, argv) => (env.production ? getProductionConfig() : getDevelopmentConfig());
module.exports = (env, argv) => (env.production ? getProductionConfig(env) : getDevelopmentConfig(env));
3 changes: 2 additions & 1 deletion fixtures/plugins/webpack/webpack.dev.js
Expand Up @@ -2,8 +2,9 @@ const EslintPlugin = require('eslint-webpack-plugin');
const merge = require('./merge.js');
const common = require('./webpack.common.js');

module.exports = () =>
module.exports = env =>
merge(common(), {
mode: env.production ? 'production' : 'development',
entry: {
main: './src/app.ts',
vendor: './src/vendor.ts',
Expand Down
2 changes: 1 addition & 1 deletion fixtures/plugins/webpack/webpack.prod.js
Expand Up @@ -3,7 +3,7 @@ const TerserWebpackPlugin = require('terser-webpack-plugin');
const merge = require('./merge.js');
const common = require('./webpack.common.js');

module.exports = () =>
module.exports = env =>
merge(common(), {
mode: 'production',
entry: './src/entry.js',
Expand Down
8 changes: 7 additions & 1 deletion fixtures/workspaces-plugin-config/knip.json
Expand Up @@ -6,6 +6,12 @@
"jest": "jest.config.js",
"babel": { "config": "rollup.config.ts" },
"workspaces": {
"packages/*": {}
"packages/*": {},
"packages/package1": {
"storybook": {
"config": "components/storybook/main.ts",
"entry": ["components/storybook/{manager,preview}.ts"]
}
}
}
}
Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions fixtures/workspaces-plugin-config/package.json
@@ -1,9 +1,13 @@
{
"name": "@workspaces-plugin-config/root",
"scripts": {
"test": "jest"
},
"workspaces": [
"packages/*"
],
"devDependencies": {
"jest": "*",
"jest-result-processor": "*",
"eslint-config-airbnb": "*"
}
Expand Down
@@ -0,0 +1 @@
export default function () {}
@@ -0,0 +1 @@
import component from './component';
@@ -1,9 +1,15 @@
{
"name": "@workspaces-plugin-config/frontend",
"scripts": {
"dev": "next dev",
"start": "next start",
"test": "vitest"
},
"dependencies": {
"next": "*"
},
"devDependencies": {
"next": "*",
"vitest": "*",
"jsdom": "*",
"@rollup/plugin-commonjs": "*"
Expand Down
@@ -0,0 +1,3 @@
export default {
files: ['**/*.ava.js'],
};
@@ -0,0 +1 @@
import component from './component';
@@ -0,0 +1 @@
export default function () {}
Empty file.
@@ -0,0 +1,3 @@
module.exports = {
stories: ['../*.tales.js'],
};
Empty file.
Empty file.
Empty file.
10 changes: 10 additions & 0 deletions fixtures/workspaces-plugin-config/packages/package1/package.json
@@ -0,0 +1,10 @@
{
"name": "@workspaces-plugin-config/package-using-ava",
"scripts": {
"test": "ava"
},
"devDependencies": {
"ava": "*",
"webpack": "*"
}
}
Empty file.
@@ -0,0 +1,13 @@
import 'webpack';

const config = [
{
entry: './production-entry.js',
},
{
mode: 'development',
entry: './dev-entry.js',
},
];

module.exports = config;
36 changes: 11 additions & 25 deletions src/WorkspaceWorker.ts
Expand Up @@ -11,13 +11,7 @@ import {
isEntryPattern,
isProductionEntryPattern,
} from './util/protocols.js';
import type {
Configuration,
EnsuredPluginConfiguration,
PluginConfiguration,
PluginName,
WorkspaceConfiguration,
} from './types/config.js';
import type { Configuration, EnsuredPluginConfiguration, PluginName, WorkspaceConfiguration } from './types/config.js';
import type { PackageJsonWithPlugins } from './types/plugins.js';
import type { InstalledBinaries, HostDependencies } from './types/workspace.js';
import type { Entries } from 'type-fest';
Expand Down Expand Up @@ -150,8 +144,9 @@ export class WorkspaceWorker {
this.hasTypesIncluded = hasTypesIncluded;
}

private getConfigForPlugin(pluginName: PluginName): PluginConfiguration {
return this.config[pluginName] !== true ? this.config[pluginName] ?? nullConfig : nullConfig;
private getConfigForPlugin(pluginName: PluginName): EnsuredPluginConfiguration {
const config = this.config[pluginName];
return typeof config === 'undefined' || typeof config === 'boolean' ? nullConfig : config;
}

getEntryFilePatterns() {
Expand Down Expand Up @@ -180,8 +175,8 @@ export class WorkspaceWorker {
const patterns: string[] = [];
for (const [pluginName, plugin] of Object.entries(plugins) as PluginNames) {
const pluginConfig = this.getConfigForPlugin(pluginName);
if (this.enabled[pluginName] && pluginConfig) {
const { entry, project } = pluginConfig === true ? nullConfig : pluginConfig;
if (this.enabled[pluginName]) {
const { entry, project } = pluginConfig;
patterns.push(...(project ?? entry ?? ('PROJECT_FILE_PATTERNS' in plugin ? plugin.PROJECT_FILE_PATTERNS : [])));
}
}
Expand All @@ -193,7 +188,7 @@ export class WorkspaceWorker {
for (const [pluginName, plugin] of Object.entries(plugins) as PluginNames) {
const pluginConfig = this.getConfigForPlugin(pluginName);
if (this.enabled[pluginName] && pluginConfig) {
const { config } = pluginConfig === true ? nullConfig : pluginConfig;
const { config } = pluginConfig;
const defaultConfigFiles = 'CONFIG_FILE_PATTERNS' in plugin ? plugin.CONFIG_FILE_PATTERNS : [];
patterns.push(...(config ?? defaultConfigFiles));
}
Expand Down Expand Up @@ -234,7 +229,7 @@ export class WorkspaceWorker {
const pluginConfig = this.getConfigForPlugin(pluginName);
if (pluginConfig) {
const defaultConfig = 'CONFIG_FILE_PATTERNS' in plugin ? plugin.CONFIG_FILE_PATTERNS : [];
return (pluginConfig === true ? null : pluginConfig.config) ?? defaultConfig;
return pluginConfig.config ?? defaultConfig;
}
return [];
}
Expand Down Expand Up @@ -267,25 +262,16 @@ export class WorkspaceWorker {

debugLogArray([name, plugin.NAME], 'config file paths', configFilePaths);

// Bail out, no config files found for this plugin
if (patterns.length > 0 && configFilePaths.length === 0) {
if (typeof pluginConfig !== 'boolean' && pluginConfig.entry !== null && pluginConfig.entry.length > 0) {
// ...but only if no entry files are set
} else {
continue;
}
}

// Plugin has no config files configured, call it once to still get the entry:/production: patterns
if (patterns.length === 0) configFilePaths.push(FAKE_PATH);
// Plugin has no config files configured, add one to still invoke it and get the entry:/production: patterns
if (configFilePaths.length === 0) configFilePaths.push(FAKE_PATH);

const pluginDependencies: Set<string> = new Set();

for (const configFilePath of configFilePaths) {
const dependencies = await plugin.findDependencies(configFilePath, {
cwd,
manifest: this.manifest,
config: pluginConfig === true ? nullConfig : pluginConfig,
config: pluginConfig,
isProduction: this.isProduction,
});

Expand Down
35 changes: 32 additions & 3 deletions src/plugins/_template/index.ts
@@ -1,5 +1,6 @@
import { timerify } from '../../util/Performance.js';
import { hasDependency, load } from '../../util/plugin.js';
import { toEntryPattern, toProductionEntryPattern } from '../../util/protocols.js';
import type { PluginConfig } from './types.js';
import type { IsPluginEnabledCallback, GenericPluginCallback } from '../../types/plugins.js';

Expand All @@ -22,9 +23,37 @@ export const PRODUCTION_ENTRY_FILE_PATTERNS = [];

export const PROJECT_FILE_PATTERNS = [];

const findPluginDependencies: GenericPluginCallback = async (configFilePath, { manifest }) => {
const config: PluginConfig = configFilePath.endsWith('package.json') ? manifest.plugin : await load(configFilePath);
return config?.plugins ?? [];
const findPluginDependencies: GenericPluginCallback = async (configFilePath, options) => {
const { manifest, config, isProduction } = options;

// load configuration file from `configFilePath` (or grab `manifest` for package.json)
// load(FAKE_PATH) will return `undefined`
const localConfig: PluginConfig | undefined = configFilePath.endsWith('package.json')
? manifest.plugin
: await load(configFilePath);

if (!localConfig) return [];

// if plugin handles entry files, order of precedence (use only the first one that's set):
// 1. config.entry
// 2. entry patterns from `cfg`
// 3. ENTRY_FILE_PATTERNS
const entryPatterns = (config?.entry ?? localConfig.entryPathsOrPatterns ?? ENTRY_FILE_PATTERNS).map(toEntryPattern);

// if PRODUCTION_ENTRY_FILE_PATTERNS is set, only return them if `config.entry` is not set
const productionPatterns = config.entry ? [] : PRODUCTION_ENTRY_FILE_PATTERNS.map(toProductionEntryPattern);

// in production mode:
// - plugins that don't deal with (production) entry files can bail out at the start of the function
// - plugins that only deal with `devDependencies` can bail out here and return all entry patterns
// - local config may include production dependencies (e.g. entry paths) and devDependencies (e.g. build tooling),
// requiring more fine-grained logic based on `isProduction`
if (isProduction) return [...entryPatterns, ...productionPatterns];

// resolve dependencies/binaries from local config file
const dependencies = localConfig?.plugins ?? [];

return [...dependencies, ...entryPatterns, ...productionPatterns];
};

export const findDependencies = timerify(findPluginDependencies);
1 change: 1 addition & 0 deletions src/plugins/_template/types.ts
@@ -1,3 +1,4 @@
export type PluginConfig = {
plugins?: string[];
entryPathsOrPatterns?: string[];
};
26 changes: 14 additions & 12 deletions src/plugins/angular/index.ts
Expand Up @@ -5,7 +5,7 @@ import { findTypeScriptDependencies } from '../typescript/index.js';
import type { AngularCLIWorkspaceConfiguration } from './types.js';
import type { IsPluginEnabledCallback, GenericPluginCallback } from '../../types/plugins.js';

// link to docs
// https://angular.io/guide/workspace-config

export const NAME = 'Angular';

Expand All @@ -16,25 +16,27 @@ export const isEnabled: IsPluginEnabledCallback = ({ dependencies }) => hasDepen

export const CONFIG_FILE_PATTERNS = ['angular.json'];

const findPluginDependencies: GenericPluginCallback = async (configFilePath, opts) => {
const { cwd } = opts;
const config: AngularCLIWorkspaceConfiguration = await load(configFilePath);
if (!config.projects) return [];
const findPluginDependencies: GenericPluginCallback = async (configFilePath, options) => {
const { cwd } = options;

const localConfig: AngularCLIWorkspaceConfiguration | undefined = await load(configFilePath);

if (!localConfig?.projects) return [];

const dependencies = new Set<string>();

for (const project of Object.values(config.projects)) {
for (const project of Object.values(localConfig.projects)) {
if (!project.architect) return [];
for (const target of Object.values(project.architect)) {
const { options } = target;
const { options: opts } = target;
const [packageName] = typeof target.builder === 'string' ? target.builder.split(':') : [];
if (typeof packageName === 'string') dependencies.add(packageName);
if (options) {
if ('main' in options && typeof options?.main === 'string') {
dependencies.add(join(cwd, options.main));
if (opts) {
if ('main' in opts && typeof opts.main === 'string') {
dependencies.add(join(cwd, opts.main));
}
if ('tsConfig' in options && typeof options.tsConfig === 'string') {
const tsConfigDependencies = await findTypeScriptDependencies(join(cwd, options.tsConfig), opts);
if ('tsConfig' in opts && typeof opts.tsConfig === 'string') {
const tsConfigDependencies = await findTypeScriptDependencies(join(cwd, opts.tsConfig), options);
tsConfigDependencies.forEach(dependency => dependencies.add(dependency));
}
}
Expand Down
22 changes: 12 additions & 10 deletions src/plugins/ava/index.ts
Expand Up @@ -30,22 +30,24 @@ export const ENTRY_FILE_PATTERNS = [
'!**/test?(s)/**/{helper,fixture}?(s)/**/*',
];

const findAvaDependencies: GenericPluginCallback = async (configFilePath, { cwd, manifest, isProduction }) => {
let config: AvaConfig = configFilePath.endsWith('package.json') ? manifest.ava : await load(configFilePath);
const findAvaDependencies: GenericPluginCallback = async (configFilePath, options) => {
const { cwd, manifest, isProduction, config } = options;

if (typeof config === 'function') config = config();
let localConfig: AvaConfig | undefined = configFilePath.endsWith('package.json')
? manifest.ava
: await load(configFilePath);

const entryPatterns = (config?.files ?? ENTRY_FILE_PATTERNS).map(toEntryPattern);
if (isProduction) return entryPatterns;
if (typeof localConfig === 'function') localConfig = localConfig();

if (!config) return [];
const entryPatterns = (config.entry ?? localConfig?.files ?? ENTRY_FILE_PATTERNS).map(toEntryPattern);

const requireArgs = (config.require ?? []).map(require => `--require ${require}`);
const otherArgs = config.nodeArguments ?? [];
if (isProduction || !localConfig) return entryPatterns;

const cmd = `node ${otherArgs.join(' ')} ${requireArgs.join(' ')}`;
const nodeArgs = localConfig.nodeArguments ?? [];
const requireArgs = (localConfig.require ?? []).map(require => `--require ${require}`);
const fakeCommand = `node ${nodeArgs.join(' ')} ${requireArgs.join(' ')}`;

const dependencies = _getDependenciesFromScripts([cmd], {
const dependencies = _getDependenciesFromScripts([fakeCommand], {
cwd,
manifest,
knownGlobalsOnly: true,
Expand Down
15 changes: 10 additions & 5 deletions src/plugins/babel/index.ts
Expand Up @@ -35,11 +35,16 @@ export const getDependenciesFromConfig = (config: BabelConfigObj): string[] => {

const findBabelDependencies: GenericPluginCallback = async (configFilePath, { manifest, isProduction }) => {
if (isProduction) return [];
let config: BabelConfig = configFilePath.endsWith('package.json') ? manifest.babel : await load(configFilePath);
if (typeof config === 'function') {
config = config(api);
}
return config ? getDependenciesFromConfig(config) : [];

let localConfig: BabelConfig | undefined = configFilePath.endsWith('package.json')
? manifest.babel
: await load(configFilePath);

if (typeof localConfig === 'function') localConfig = localConfig(api);

if (!localConfig) return [];

return getDependenciesFromConfig(localConfig);
};

export const findDependencies = timerify(findBabelDependencies);
8 changes: 6 additions & 2 deletions src/plugins/capacitor/index.ts
Expand Up @@ -16,8 +16,12 @@ export const CONFIG_FILE_PATTERNS = ['capacitor.config.ts'];

const findCapacitorDependencies: GenericPluginCallback = async (configFilePath, { isProduction }) => {
if (isProduction) return [];
const config: CapacitorConfig = await load(configFilePath);
return config.includePlugins ?? [];

const localConfig: CapacitorConfig | undefined = await load(configFilePath);

if (!localConfig) return [];

return localConfig.includePlugins ?? [];
};

export const findDependencies = timerify(findCapacitorDependencies);

0 comments on commit 9fd764b

Please sign in to comment.