@roots/bud-build 6.17.0 → 6.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -109,9 +109,6 @@ However, the amount of effort needed to maintain and develop new features and pr
109
109
  <a href="https://wordpress.com/">
110
110
  <img src="https://cdn.roots.io/app/uploads/wordpress.svg" alt="WordPress.com" width="200" height="150"/>
111
111
  </a>
112
- <a href="https://pantheon.io/">
113
- <img src="https://cdn.roots.io/app/uploads/pantheon.svg" alt="Pantheon" width="200" height="150"/>
114
- </a>
115
112
  <a href="https://worksitesafety.ca/careers/">
116
113
  <img src="https://cdn.roots.io/app/uploads/worksite-safety.svg" alt="Worksite Safety" width="200" height="150"/>
117
114
  </a>
@@ -1,4 +1,25 @@
1
- import isUndefined from '@roots/bud-support/lodash/isUndefined';
2
- export const dependencies = async ({ hooks, root, }) => hooks
3
- .filter(`build.dependencies`, [])
4
- .filter(label => !isUndefined(root.children[label]));
1
+ import { ConfigError } from '@roots/bud-support/errors';
2
+ import isUndefined from '@roots/bud-support/isUndefined';
3
+ export const dependencies = async ({ hooks, label, root, }) => {
4
+ const dependencies = hooks
5
+ .filter(`build.dependencies`, [])
6
+ ?.filter(dependency => {
7
+ const defined = !isUndefined(root.children?.[dependency]);
8
+ if (!defined) {
9
+ throw ConfigError.normalize(`${dependency} is not a registered instance of bud.js.`, {
10
+ details: root.children
11
+ ? `Available instances are: ${Object.values(root.children)
12
+ .map(child => child.label)
13
+ .join(`, `)}`
14
+ : `We would tell you what the available instances are, but there are none registered.`,
15
+ docs: new URL(`https://bud.js.org/learn/general-use/multi-instance`),
16
+ instance: label,
17
+ thrownBy: `@roots/bud-build/config/dependencies`,
18
+ });
19
+ }
20
+ return true;
21
+ });
22
+ if (!dependencies || dependencies?.length < 1)
23
+ return undefined;
24
+ return dependencies;
25
+ };
@@ -1,5 +1,7 @@
1
1
  export const entry = async ({ hooks }) => {
2
- const entrypoints = hooks.filter(`build.entry`, { main: { import: [`index`] } });
2
+ const entrypoints = hooks.filter(`build.entry`, {
3
+ main: { import: [`./index`] },
4
+ });
3
5
  return Object.entries(entrypoints).reduce((acc, [key, value]) => {
4
6
  value.import = [...new Set(value.import)];
5
7
  return { ...acc, [key]: value };
@@ -3,16 +3,16 @@ export const infrastructureLogging = async (bud) => bud.hooks.filter(`build.infr
3
3
  console: bud.hooks.filter(`build.infrastructureLogging.console`, {
4
4
  ...console,
5
5
  error: (...args) => {
6
- logger.scope(bud.label, `webpack`).error(...args);
6
+ logger.unscope().error(...args);
7
7
  },
8
8
  info: (...args) => {
9
- logger.scope(bud.label, `webpack`).info(...args);
9
+ logger.unscope().info(...args);
10
10
  },
11
11
  log: (...args) => {
12
- logger.scope(bud.label, `webpack`).log(...args);
12
+ logger.unscope().log(...args);
13
13
  },
14
14
  warn: (...args) => {
15
- logger.scope(bud.label, `webpack`).info(...args);
15
+ logger.unscope().info(...args);
16
16
  },
17
17
  }),
18
18
  level: bud.hooks.filter(`build.infrastructureLogging.level`, `log`),
@@ -3,21 +3,115 @@ export const module = async ({ build: { rules }, hooks: { filter }, path, }) =>
3
3
  rules: getRules({ filter, path, rules }),
4
4
  unsafeCache: getUnsafeCache(filter),
5
5
  });
6
- const getRules = ({ filter, path, rules }) => [
7
- ...filter(`build.module.rules.before`, [
6
+ /**
7
+ * Get all module.rules
8
+ */
9
+ const getRules = ({ filter, path, rules }) => {
10
+ return [
11
+ ...filter(`build.module.rules.before`, [
12
+ {
13
+ include: [path(`@src`)],
14
+ parser: { requireEnsure: false },
15
+ test: filter(`pattern.js`),
16
+ },
17
+ rules.image.toWebpack
18
+ ? {
19
+ oneOf: [
20
+ rules[`inline-image`]?.toWebpack?.(),
21
+ rules.image.toWebpack?.(),
22
+ ].filter(Boolean),
23
+ test: filter(`pattern.image`),
24
+ }
25
+ : undefined,
26
+ rules.font.toWebpack
27
+ ? {
28
+ oneOf: [
29
+ rules[`inline-font`]?.toWebpack?.(),
30
+ rules.font.toWebpack(),
31
+ ].filter(Boolean),
32
+ test: filter(`pattern.font`),
33
+ }
34
+ : undefined,
35
+ rules.svg.toWebpack
36
+ ? {
37
+ oneOf: [
38
+ rules[`inline-svg`]?.toWebpack?.(),
39
+ rules.svg.toWebpack(),
40
+ ].filter(Boolean),
41
+ test: filter(`pattern.svg`),
42
+ }
43
+ : undefined,
44
+ ]),
8
45
  {
9
- include: [path(`@src`)],
10
- parser: { requireEnsure: false },
11
- test: filter(`pattern.js`),
46
+ oneOf: [
47
+ ...filter(`build.module.rules.oneOf`, [
48
+ ...getDefinedRules({ rules }),
49
+ ...makeIssuerRuleSet({ filter, path, rules }),
50
+ ]),
51
+ ].filter(Boolean),
12
52
  },
13
- ]),
14
- {
15
- oneOf: filter(`build.module.rules.oneOf`, Object.values(rules)
16
- .filter(Boolean)
17
- .map(rule => (`toWebpack` in rule ? rule.toWebpack() : rule))),
18
- },
19
- ...filter(`build.module.rules.after`, []),
53
+ ...filter(`build.module.rules.after`, []),
54
+ ].filter(Boolean);
55
+ };
56
+ /**
57
+ * Get the standard rules defined in the bud config, extensions, etc.
58
+ */
59
+ const getDefinedRules = ({ rules }) => {
60
+ return [
61
+ ...Object.entries(rules)
62
+ .filter(([key, _]) => {
63
+ return !DEFINED.includes(key) && !RESOURCES.includes(key);
64
+ })
65
+ .map(([_, value]) => value),
66
+ ...DEFINED.map(key => rules[key]),
67
+ ]
68
+ .filter(Boolean)
69
+ .map(rule => (`toWebpack` in rule ? rule.toWebpack() : rule));
70
+ };
71
+ const RESOURCES = [
72
+ `image`,
73
+ `font`,
74
+ `svg`,
75
+ `inline-font`,
76
+ `inline-image`,
77
+ `inline-svg`,
20
78
  ];
79
+ const DEFINED = [
80
+ `csv`,
81
+ `toml`,
82
+ `yml`,
83
+ `json`,
84
+ `html`,
85
+ `webp`,
86
+ `scss-module`,
87
+ `scss`,
88
+ `css-module`,
89
+ `css`,
90
+ `vue`,
91
+ `js`,
92
+ `ts`,
93
+ ];
94
+ /**
95
+ * Get rules for css and css-module imports issued by non-css files.
96
+ */
97
+ const makeIssuerRuleSet = ({ filter, path, rules }) => {
98
+ const results = [];
99
+ rules[`css-module`]?.toWebpack?.().use &&
100
+ results.push({
101
+ exclude: [path(`@src`)],
102
+ issuer: { not: filter(`pattern.cssModule`) },
103
+ test: filter(`pattern.cssModule`),
104
+ use: rules[`css-module`].toWebpack?.().use,
105
+ });
106
+ rules[`css`]?.toWebpack?.().use &&
107
+ results.push({
108
+ exclude: [path(`@src`)],
109
+ issuer: { not: filter(`pattern.css`) },
110
+ test: filter(`pattern.css`),
111
+ use: rules[`css`].toWebpack?.().use,
112
+ });
113
+ return results;
114
+ };
21
115
  const getNoParse = (filter) => filter(`build.module.noParse`, undefined);
22
116
  /**
23
117
  * By leaving undefined, webpack will strongly cache parsed modules from node_modules
@@ -21,7 +21,12 @@ export const output = async ({ hooks: { filter }, isProduction, path, relPath, }
21
21
  * @see {@link https://medium.com/@kenneth_chau/speeding-up-webpack-typescript-incremental-builds-by-7x-3912ba4c1d15}
22
22
  */
23
23
  pathinfo: filter(`build.output.pathinfo`, false),
24
- publicPath: filter(`build.output.publicPath`, `auto`),
24
+ publicPath: (() => {
25
+ const value = filter(`build.output.publicPath`, `auto`);
26
+ if ([``, `auto`].includes(value))
27
+ return value;
28
+ return value.endsWith(`/`) ? value : `${value}/`;
29
+ })(),
25
30
  scriptType: filter(`build.output.scriptType`, undefined),
26
31
  uniqueName: filter(`build.output.uniqueName`, `@roots/bud`),
27
32
  });
@@ -1,12 +1,12 @@
1
- import isString from '@roots/bud-support/lodash/isString';
1
+ import isString from '@roots/bud-support/isString';
2
2
  export const resolve = async (bud) => {
3
3
  return await bud.hooks.filterAsync(`build.resolve`, {
4
4
  alias: {
5
- [`@src`]: bud.path(`@src`),
5
+ '@src': bud.path(`@src`),
6
6
  ...(await bud.hooks.filterAsync(`build.resolve.alias`, {})),
7
7
  },
8
8
  extensionAlias: await bud.hooks.filterAsync(`build.resolve.extensionAlias`, {
9
- [`.js`]: [`.ts`, `.tsx`, `.js`],
9
+ [`.js`]: [`.ts`, `.tsx`, `.js`, `.jsx`],
10
10
  [`.mjs`]: [`.mts`, `.mtx`, `.mjs`],
11
11
  }),
12
12
  extensions: Array.from(bud.hooks.filter(`build.resolve.extensions`, new Set([`.js`, `.mjs`, `.css`]))),
@@ -14,6 +14,7 @@ export const resolve = async (bud) => {
14
14
  bud.hooks.filter(`location.@src`),
15
15
  bud.hooks.filter(`location.@modules`),
16
16
  ].filter(v => isString(v) && v.length > 0)),
17
+ symlinks: bud.hooks.filter(`build.resolve.symlinks`, undefined),
17
18
  /**
18
19
  * Leave `undefined` to use webpack default (true in dev, false in production)
19
20
  */
@@ -1,8 +1,14 @@
1
- export const resolveLoader = async ({ hooks, module, }) => hooks.filter(`build.resolveLoader`, {
2
- alias: hooks.filter(`build.resolveLoader.alias`, {
3
- 'css-loader': await module.resolve(`@roots/bud-support/css-loader`),
4
- 'file-loader': await module.resolve(`@roots/bud-support/file-loader`),
5
- 'html-loader': await module.resolve(`@roots/bud-support/html-loader`),
6
- 'style-loader': await module.resolve(`@roots/bud-support/style-loader`),
7
- }),
8
- });
1
+ export const resolveLoader = async ({ hooks, module, }) => {
2
+ const result = hooks.filter(`build.resolveLoader`, {
3
+ alias: hooks.filter(`build.resolveLoader.alias`, {
4
+ 'css-loader': await module.resolve(`@roots/bud-support/css-loader`),
5
+ 'file-loader': await module.resolve(`@roots/bud-support/file-loader`),
6
+ 'html-loader': await module.resolve(`@roots/bud-support/html-loader`),
7
+ 'style-loader': await module.resolve(`@roots/bud-support/style-loader`),
8
+ }),
9
+ });
10
+ const modules = hooks.filter(`build.resolveLoader.modules`, undefined);
11
+ if (modules)
12
+ result.modules = modules;
13
+ return result;
14
+ };
@@ -1,6 +1,6 @@
1
1
  import { __decorate } from "tslib";
2
2
  import { bind } from '@roots/bud-support/decorators/bind';
3
- import isFunction from '@roots/bud-support/lodash/isFunction';
3
+ import isFunction from '@roots/bud-support/isFunction';
4
4
  class Registrable {
5
5
  _app;
6
6
  /**
package/lib/index.d.ts CHANGED
@@ -41,18 +41,22 @@ declare module '@roots/bud-framework' {
41
41
  }
42
42
  interface Rules {
43
43
  [`css-module`]: Rule;
44
+ [`inline-font`]: Rule;
45
+ [`inline-image`]: Rule;
46
+ [`inline-svg`]: Rule;
44
47
  css: Rule;
45
48
  csv: Rule;
46
49
  font: Rule;
47
50
  html: Rule;
48
51
  image: Rule;
49
- inlineFont: Rule;
50
- inlineImage: Rule;
51
- inlineSvg: Rule;
52
52
  js: Rule;
53
53
  json: Rule;
54
+ sass: Rule;
54
55
  svg: Rule;
55
56
  toml: Rule;
57
+ ts: Rule;
58
+ vue: Rule;
59
+ webp: Rule;
56
60
  yml: Rule;
57
61
  }
58
62
  }
package/lib/item/index.js CHANGED
@@ -3,7 +3,8 @@ import { basename } from 'node:path';
3
3
  import Registrable from '@roots/bud-build/helpers/registrable';
4
4
  import Loader from '@roots/bud-build/loader';
5
5
  import { bind } from '@roots/bud-support/decorators/bind';
6
- import isString from '@roots/bud-support/lodash/isString';
6
+ import isString from '@roots/bud-support/isString';
7
+ import logger from '@roots/bud-support/logger';
7
8
  /**
8
9
  * Item class
9
10
  */
@@ -98,7 +99,7 @@ class Item extends Registrable {
98
99
  output.ident = this.getIdent();
99
100
  }
100
101
  if (!output.loader) {
101
- this.app.error(`error in ${this.ident}`, `no loader registered`);
102
+ logger.error(`error in ${this.ident}`, `no loader registered`);
102
103
  }
103
104
  return Object.entries(output).reduce((output, [key, value]) => ({
104
105
  ...(output ?? {}),
@@ -1,4 +1,4 @@
1
- import isBoolean from '@roots/bud-support/lodash/isBoolean';
1
+ import isBoolean from '@roots/bud-support/isBoolean';
2
2
  /**
3
3
  * CSS loader
4
4
  */
@@ -1,7 +1,7 @@
1
1
  import * as items from '@roots/bud-build/items';
2
2
  import * as loaders from '@roots/bud-build/loaders';
3
3
  import * as rules from '@roots/bud-build/rules';
4
- import kebabCase from '@roots/bud-support/lodash/kebabCase';
4
+ import kebabCase from '@roots/bud-support/kebabCase';
5
5
  /**
6
6
  * Register built-in {@link loaders}, {@link items} and {@link rules}
7
7
  */
@@ -16,7 +16,9 @@ async function register(bud) {
16
16
  bud.build.items.precss = bud.isProduction
17
17
  ? bud.build.items.minicss
18
18
  : bud.build.items.style;
19
- Object.entries(rules).map(makeRegister(bud, bud.build.setRule));
19
+ await Promise.all(Object.entries(rules).map(makeRegister(bud, bud.build.setRule))).catch(e => {
20
+ throw e;
21
+ });
20
22
  }
21
23
  /**
22
24
  * Registry factory
@@ -18,6 +18,10 @@ declare class Rule extends Registrable implements Interface {
18
18
  * RuleSetRule include
19
19
  */
20
20
  include?: Options[`include`];
21
+ /**
22
+ * RuleSetRule issuer
23
+ */
24
+ issuer?: Options[`issuer`];
21
25
  /**
22
26
  * RuleSetRule parser
23
27
  */
@@ -60,6 +64,10 @@ declare class Rule extends Registrable implements Interface {
60
64
  * Get `include` value
61
65
  */
62
66
  getInclude(): Array<RegExp | string>;
67
+ /**
68
+ * Get `issuer` value
69
+ */
70
+ getIssuer(): Output['issuer'];
63
71
  /**
64
72
  * Get `parser` value
65
73
  */
@@ -96,6 +104,10 @@ declare class Rule extends Registrable implements Interface {
96
104
  * Set `include` value
97
105
  */
98
106
  setInclude(includes: Options['include']): this;
107
+ /**
108
+ * Set `issuer` value
109
+ */
110
+ setIssuer(issuer: Options['issuer']): this;
99
111
  /**
100
112
  * Set `parser` value
101
113
  */
package/lib/rule/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { __decorate } from "tslib";
2
2
  import Registrable from '@roots/bud-build/helpers/registrable';
3
3
  import { bind } from '@roots/bud-support/decorators/bind';
4
- import isFunction from '@roots/bud-support/lodash/isFunction';
5
- import isString from '@roots/bud-support/lodash/isString';
4
+ import isFunction from '@roots/bud-support/isFunction';
5
+ import isString from '@roots/bud-support/isString';
6
+ import logger from '@roots/bud-support/logger';
6
7
  /**
7
8
  * RuleSetRule
8
9
  */
@@ -19,6 +20,10 @@ class Rule extends Registrable {
19
20
  * RuleSetRule include
20
21
  */
21
22
  include;
23
+ /**
24
+ * RuleSetRule issuer
25
+ */
26
+ issuer;
22
27
  /**
23
28
  * RuleSetRule parser
24
29
  */
@@ -78,6 +83,12 @@ class Rule extends Registrable {
78
83
  getInclude() {
79
84
  return this.include?.map(this.unwrap);
80
85
  }
86
+ /**
87
+ * Get `issuer` value
88
+ */
89
+ getIssuer() {
90
+ return this.issuer;
91
+ }
81
92
  /**
82
93
  * Get `parser` value
83
94
  */
@@ -137,6 +148,13 @@ class Rule extends Registrable {
137
148
  this.include = isFunction(includes) ? includes(this.include) : includes;
138
149
  return this;
139
150
  }
151
+ /**
152
+ * Set `issuer` value
153
+ */
154
+ setIssuer(issuer) {
155
+ this.issuer = issuer;
156
+ return this;
157
+ }
140
158
  /**
141
159
  * Set `parser` value
142
160
  */
@@ -189,6 +207,7 @@ class Rule extends Registrable {
189
207
  exclude: this.getExclude(),
190
208
  generator: this.getGenerator(),
191
209
  include: this.getInclude(),
210
+ issuer: this.getIssuer(),
192
211
  parser: this.getParser(),
193
212
  resolve: this.getResolve(),
194
213
  resourceQuery: this.getResourceQuery(),
@@ -206,7 +225,7 @@ class Rule extends Registrable {
206
225
  return a;
207
226
  return { ...a, [k]: v };
208
227
  }, {});
209
- this.app.info(`built rule`, output);
228
+ logger.info(`built rule`, output);
210
229
  return output;
211
230
  }
212
231
  }
@@ -219,6 +238,9 @@ __decorate([
219
238
  __decorate([
220
239
  bind
221
240
  ], Rule.prototype, "getInclude", null);
241
+ __decorate([
242
+ bind
243
+ ], Rule.prototype, "getIssuer", null);
222
244
  __decorate([
223
245
  bind
224
246
  ], Rule.prototype, "getParser", null);
@@ -243,6 +265,9 @@ __decorate([
243
265
  __decorate([
244
266
  bind
245
267
  ], Rule.prototype, "setInclude", null);
268
+ __decorate([
269
+ bind
270
+ ], Rule.prototype, "setIssuer", null);
246
271
  __decorate([
247
272
  bind
248
273
  ], Rule.prototype, "setParser", null);
@@ -1,5 +1,4 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import type { Factory } from '@roots/bud-build/registry';
3
2
  declare const inlineSvg: Factory;
4
- declare const dataUrl: (data: Buffer) => string;
3
+ declare const dataUrl: (data: Uint8Array) => string;
5
4
  export { dataUrl, inlineSvg as default };
@@ -5,8 +5,8 @@ import { register } from '@roots/bud-build/registry';
5
5
  import Rule, {} from '@roots/bud-build/rule';
6
6
  import { Service } from '@roots/bud-framework/service';
7
7
  import { bind } from '@roots/bud-support/decorators/bind';
8
- import isFunction from '@roots/bud-support/lodash/isFunction';
9
- import isUndefined from '@roots/bud-support/lodash/isUndefined';
8
+ import isFunction from '@roots/bud-support/isFunction';
9
+ import isUndefined from '@roots/bud-support/isUndefined';
10
10
  /**
11
11
  * {@link BudBuild}
12
12
  */
@@ -55,22 +55,32 @@ class Build extends Service {
55
55
  async make() {
56
56
  this.logger.log(`bud.build.make called`);
57
57
  await this.app.hooks.fire(`build.before`, this.app);
58
- await import(`@roots/bud-build/config`).then(async (records) => await Promise.all(Object.entries(records).map(async ([prop, factory]) => {
59
- try {
60
- const value = await factory(this.app);
61
- if (isUndefined(value))
62
- return;
63
- this.config[prop] = value;
64
- this.logger.success(`built`, prop);
58
+ await import(`@roots/bud-build/config`)
59
+ .then(async (records) => await Promise.all(Object.entries(records).map(async ([prop, factory]) => {
60
+ const value = await factory(this.app).catch(this.catch);
61
+ if (isUndefined(value)) {
62
+ this.logger.success(`omitting:`, prop, `(undefined)`);
63
+ return;
65
64
  }
66
- catch (error) {
67
- throw error;
68
- }
69
- })));
70
- this.logger.success(`configuration successfully built`);
65
+ Object.defineProperty(this.config, prop, {
66
+ configurable: true,
67
+ enumerable: true,
68
+ value,
69
+ writable: true,
70
+ });
71
+ this.logger
72
+ .success(`defined:`, prop, `(${typeof this.config[prop]})`)
73
+ .info(prop, `info:`, this.config[prop]);
74
+ })))
75
+ .catch(this.catch);
76
+ this.logger.success(`configuration built`);
71
77
  this.logger.info(this.config);
72
- await this.app.hooks.fire(`build.after`, this.app);
73
- return this.config;
78
+ await this.app.hooks.fire(`build.after`, this.app).catch(this.catch);
79
+ return Object.entries(this.config).reduce((a, [k, v]) => {
80
+ if (isUndefined(v))
81
+ return a;
82
+ return { ...a, [k]: v };
83
+ }, {});
74
84
  }
75
85
  /**
76
86
  * {@link BudBuild.makeItem}
@@ -94,6 +104,7 @@ class Build extends Service {
94
104
  * {@link BudBuild.setItem}
95
105
  */
96
106
  setItem(ident, definition) {
107
+ this.logger.log(`build.setItem`, ident);
97
108
  const maybeOptionsCallback = isUndefined(definition)
98
109
  ? { ident, loader: ident }
99
110
  : definition;
@@ -101,36 +112,41 @@ class Build extends Service {
101
112
  ? maybeOptionsCallback(this.makeItem())
102
113
  : this.makeItem(maybeOptionsCallback);
103
114
  this.items[ident] = item;
104
- this.logger.info(`set item`, item);
115
+ this.logger.info(item);
105
116
  return this;
106
117
  }
107
118
  /**
108
119
  * {@link BudBuild.setLoader}
109
120
  */
110
121
  setLoader(name, definition) {
122
+ this.logger.log(`build.setLoader`, name);
111
123
  const loader = isUndefined(definition)
112
124
  ? this.makeLoader(name)
113
125
  : definition instanceof Loader
114
126
  ? definition
115
127
  : this.makeLoader(definition);
116
128
  this.loaders[name] = loader;
117
- this.logger.info(`set loader`, loader);
129
+ this.logger.info(loader);
118
130
  return this;
119
131
  }
120
132
  /**
121
133
  * {@link BudBuild.setRule}
122
134
  */
123
135
  setRule(name, definition) {
136
+ this.logger.log(`build.setRule`, name);
124
137
  const rule = definition instanceof Rule
125
138
  ? definition
126
139
  : isFunction(definition)
127
140
  ? definition(this.makeRule())
128
141
  : this.makeRule(definition);
129
142
  this.rules[name] = rule;
130
- this.logger.info(`set rule`, rule);
143
+ this.logger.info(rule);
131
144
  return this;
132
145
  }
133
146
  }
147
+ __decorate([
148
+ bind
149
+ ], Build.prototype, "bootstrap", null);
134
150
  __decorate([
135
151
  bind
136
152
  ], Build.prototype, "getItem", null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roots/bud-build",
3
- "version": "6.17.0",
3
+ "version": "6.19.0",
4
4
  "description": "bud.js core module",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -169,14 +169,14 @@
169
169
  "types": "./lib/index.d.ts",
170
170
  "module": "./lib/index.js",
171
171
  "devDependencies": {
172
- "@roots/bud-api": "6.17.0",
173
- "@roots/bud-hooks": "6.17.0",
172
+ "@roots/bud-api": "6.19.0",
173
+ "@roots/bud-hooks": "6.19.0",
174
174
  "@skypack/package-check": "0.2.2",
175
- "@types/node": "18.17.19"
175
+ "@types/node": "20.10.4"
176
176
  },
177
177
  "dependencies": {
178
- "@roots/bud-framework": "6.17.0",
179
- "@roots/bud-support": "6.17.0",
178
+ "@roots/bud-framework": "6.19.0",
179
+ "@roots/bud-support": "6.19.0",
180
180
  "tslib": "2.6.2"
181
181
  },
182
182
  "volta": {
@@ -1,11 +1,40 @@
1
1
  import type {Factory} from '@roots/bud-build/config'
2
2
 
3
- import isUndefined from '@roots/bud-support/lodash/isUndefined'
3
+ import {ConfigError} from '@roots/bud-support/errors'
4
+ import isUndefined from '@roots/bud-support/isUndefined'
4
5
 
5
6
  export const dependencies: Factory<`dependencies`> = async ({
6
7
  hooks,
8
+ label,
7
9
  root,
8
- }) =>
9
- hooks
10
+ }) => {
11
+ const dependencies = hooks
10
12
  .filter(`build.dependencies`, [])
11
- .filter(label => !isUndefined(root.children[label]))
13
+ ?.filter(dependency => {
14
+ const defined = !isUndefined(root.children?.[dependency])
15
+
16
+ if (!defined) {
17
+ throw ConfigError.normalize(
18
+ `${dependency} is not a registered instance of bud.js.`,
19
+ {
20
+ details: root.children
21
+ ? `Available instances are: ${Object.values(root.children)
22
+ .map(child => child.label)
23
+ .join(`, `)}`
24
+ : `We would tell you what the available instances are, but there are none registered.`,
25
+ docs: new URL(
26
+ `https://bud.js.org/learn/general-use/multi-instance`,
27
+ ),
28
+ instance: label,
29
+ thrownBy: `@roots/bud-build/config/dependencies`,
30
+ },
31
+ )
32
+ }
33
+
34
+ return true
35
+ })
36
+
37
+ if (!dependencies || dependencies?.length < 1) return undefined
38
+
39
+ return dependencies
40
+ }
@@ -1,7 +1,9 @@
1
1
  import type {Factory} from '@roots/bud-build/config'
2
2
 
3
3
  export const entry: Factory<`entry`> = async ({hooks}) => {
4
- const entrypoints = hooks.filter(`build.entry`, {main: {import: [`index`]}})
4
+ const entrypoints = hooks.filter(`build.entry`, {
5
+ main: {import: [`./index`]},
6
+ })
5
7
 
6
8
  return Object.entries(entrypoints).reduce((acc, [key, value]) => {
7
9
  value.import = [...new Set(value.import)]
@@ -9,16 +9,16 @@ export const infrastructureLogging: Factory<
9
9
  console: bud.hooks.filter(`build.infrastructureLogging.console`, {
10
10
  ...console,
11
11
  error: (...args: any[]) => {
12
- logger.scope(bud.label, `webpack`).error(...args)
12
+ logger.unscope().error(...args)
13
13
  },
14
14
  info: (...args: any[]) => {
15
- logger.scope(bud.label, `webpack`).info(...args)
15
+ logger.unscope().info(...args)
16
16
  },
17
17
  log: (...args: any[]) => {
18
- logger.scope(bud.label, `webpack`).log(...args)
18
+ logger.unscope().log(...args)
19
19
  },
20
20
  warn: (...args: any[]) => {
21
- logger.scope(bud.label, `webpack`).info(...args)
21
+ logger.unscope().info(...args)
22
22
  },
23
23
  }),
24
24
  level: bud.hooks.filter(`build.infrastructureLogging.level`, `log`),
@@ -1,5 +1,5 @@
1
1
  import type {Factory} from '@roots/bud-build/config'
2
- import type {Bud} from '@roots/bud-framework'
2
+ import type {Bud, RuleSetRule} from '@roots/bud-framework'
3
3
 
4
4
  interface Props {
5
5
  filter: Bud[`hooks`][`filter`]
@@ -18,25 +18,125 @@ export const module: Factory<`module`> = async ({
18
18
  unsafeCache: getUnsafeCache(filter),
19
19
  })
20
20
 
21
- const getRules = ({filter, path, rules}: Props) => [
22
- ...filter(`build.module.rules.before`, [
21
+ /**
22
+ * Get all module.rules
23
+ */
24
+ const getRules = ({filter, path, rules}: Props): Array<RuleSetRule> => {
25
+ return [
26
+ ...filter(`build.module.rules.before`, [
27
+ {
28
+ include: [path(`@src`)],
29
+ parser: {requireEnsure: false},
30
+ test: filter(`pattern.js`),
31
+ },
32
+ rules.image.toWebpack
33
+ ? {
34
+ oneOf: [
35
+ rules[`inline-image`]?.toWebpack?.(),
36
+ rules.image.toWebpack?.(),
37
+ ].filter(Boolean),
38
+ test: filter(`pattern.image`),
39
+ }
40
+ : undefined,
41
+
42
+ rules.font.toWebpack
43
+ ? {
44
+ oneOf: [
45
+ rules[`inline-font`]?.toWebpack?.(),
46
+ rules.font.toWebpack(),
47
+ ].filter(Boolean),
48
+ test: filter(`pattern.font`),
49
+ }
50
+ : undefined,
51
+
52
+ rules.svg.toWebpack
53
+ ? {
54
+ oneOf: [
55
+ rules[`inline-svg`]?.toWebpack?.(),
56
+ rules.svg.toWebpack(),
57
+ ].filter(Boolean),
58
+ test: filter(`pattern.svg`),
59
+ }
60
+ : undefined,
61
+ ]),
23
62
  {
24
- include: [path(`@src`)],
25
- parser: {requireEnsure: false},
26
- test: filter(`pattern.js`),
63
+ oneOf: [
64
+ ...filter(`build.module.rules.oneOf`, [
65
+ ...getDefinedRules({rules}),
66
+ ...makeIssuerRuleSet({filter, path, rules}),
67
+ ]),
68
+ ].filter(Boolean),
27
69
  },
28
- ]),
29
- {
30
- oneOf: filter(
31
- `build.module.rules.oneOf`,
32
- Object.values(rules)
33
- .filter(Boolean)
34
- .map(rule => (`toWebpack` in rule ? rule.toWebpack() : rule)),
35
- ),
36
- },
37
- ...filter(`build.module.rules.after`, []),
70
+ ...filter(`build.module.rules.after`, []),
71
+ ].filter(Boolean)
72
+ }
73
+
74
+ /**
75
+ * Get the standard rules defined in the bud config, extensions, etc.
76
+ */
77
+ const getDefinedRules = ({rules}: Partial<Props>) => {
78
+ return [
79
+ ...Object.entries(rules)
80
+ .filter(([key, _]) => {
81
+ return !DEFINED.includes(key) && !RESOURCES.includes(key)
82
+ })
83
+ .map(([_, value]) => value),
84
+ ...DEFINED.map(key => rules[key]),
85
+ ]
86
+ .filter(Boolean)
87
+ .map(rule => (`toWebpack` in rule ? rule.toWebpack() : rule))
88
+ }
89
+
90
+ const RESOURCES = [
91
+ `image`,
92
+ `font`,
93
+ `svg`,
94
+ `inline-font`,
95
+ `inline-image`,
96
+ `inline-svg`,
38
97
  ]
39
98
 
99
+ const DEFINED = [
100
+ `csv`,
101
+ `toml`,
102
+ `yml`,
103
+ `json`,
104
+ `html`,
105
+ `webp`,
106
+ `scss-module`,
107
+ `scss`,
108
+ `css-module`,
109
+ `css`,
110
+ `vue`,
111
+ `js`,
112
+ `ts`,
113
+ ]
114
+
115
+ /**
116
+ * Get rules for css and css-module imports issued by non-css files.
117
+ */
118
+ const makeIssuerRuleSet = ({filter, path, rules}: Props) => {
119
+ const results = []
120
+
121
+ rules[`css-module`]?.toWebpack?.().use &&
122
+ results.push({
123
+ exclude: [path(`@src`)],
124
+ issuer: {not: filter(`pattern.cssModule`)},
125
+ test: filter(`pattern.cssModule`),
126
+ use: rules[`css-module`].toWebpack?.().use,
127
+ })
128
+
129
+ rules[`css`]?.toWebpack?.().use &&
130
+ results.push({
131
+ exclude: [path(`@src`)],
132
+ issuer: {not: filter(`pattern.css`)},
133
+ test: filter(`pattern.css`),
134
+ use: rules[`css`].toWebpack?.().use,
135
+ })
136
+
137
+ return results
138
+ }
139
+
40
140
  const getNoParse = (filter: Props[`filter`]) =>
41
141
  filter(`build.module.noParse`, undefined)
42
142
 
@@ -30,7 +30,11 @@ export const output: Factory<`output`> = async ({
30
30
  * @see {@link https://medium.com/@kenneth_chau/speeding-up-webpack-typescript-incremental-builds-by-7x-3912ba4c1d15}
31
31
  */
32
32
  pathinfo: filter(`build.output.pathinfo`, false),
33
- publicPath: filter(`build.output.publicPath`, `auto`),
33
+ publicPath: (() => {
34
+ const value = filter(`build.output.publicPath`, `auto`)
35
+ if ([``, `auto`].includes(value)) return value
36
+ return value.endsWith(`/`) ? value : `${value}/`
37
+ })(),
34
38
  scriptType: filter(`build.output.scriptType`, undefined),
35
39
  uniqueName: filter(`build.output.uniqueName`, `@roots/bud`),
36
40
  })
@@ -1,18 +1,18 @@
1
1
  import type {Factory} from '@roots/bud-build/config'
2
2
 
3
- import isString from '@roots/bud-support/lodash/isString'
3
+ import isString from '@roots/bud-support/isString'
4
4
 
5
5
  export const resolve: Factory<`resolve`> = async bud => {
6
6
  return await bud.hooks.filterAsync(`build.resolve`, {
7
7
  alias: {
8
- [`@src`]: bud.path(`@src`),
8
+ '@src': bud.path(`@src`),
9
9
  ...(await bud.hooks.filterAsync(`build.resolve.alias`, {})),
10
10
  },
11
11
 
12
12
  extensionAlias: await bud.hooks.filterAsync(
13
13
  `build.resolve.extensionAlias`,
14
14
  {
15
- [`.js`]: [`.ts`, `.tsx`, `.js`],
15
+ [`.js`]: [`.ts`, `.tsx`, `.js`, `.jsx`],
16
16
  [`.mjs`]: [`.mts`, `.mtx`, `.mjs`],
17
17
  },
18
18
  ),
@@ -32,6 +32,8 @@ export const resolve: Factory<`resolve`> = async bud => {
32
32
  ].filter(v => isString(v) && v.length > 0),
33
33
  ),
34
34
 
35
+ symlinks: bud.hooks.filter(`build.resolve.symlinks`, undefined),
36
+
35
37
  /**
36
38
  * Leave `undefined` to use webpack default (true in dev, false in production)
37
39
  */
@@ -3,8 +3,8 @@ import type {Factory} from '@roots/bud-build/config'
3
3
  export const resolveLoader: Factory<`resolveLoader`> = async ({
4
4
  hooks,
5
5
  module,
6
- }) =>
7
- hooks.filter(`build.resolveLoader`, {
6
+ }) => {
7
+ const result = hooks.filter(`build.resolveLoader`, {
8
8
  alias: hooks.filter(`build.resolveLoader.alias`, {
9
9
  'css-loader': await module.resolve(`@roots/bud-support/css-loader`),
10
10
  'file-loader': await module.resolve(
@@ -18,3 +18,9 @@ export const resolveLoader: Factory<`resolveLoader`> = async ({
18
18
  ),
19
19
  }),
20
20
  })
21
+
22
+ const modules = hooks.filter(`build.resolveLoader.modules`, undefined)
23
+ if (modules) result.modules = modules
24
+
25
+ return result
26
+ }
@@ -2,7 +2,7 @@ import type {Bud} from '@roots/bud-framework'
2
2
  import type {Base as BuildBase} from '@roots/bud-framework/services/build'
3
3
 
4
4
  import {bind} from '@roots/bud-support/decorators/bind'
5
- import isFunction from '@roots/bud-support/lodash/isFunction'
5
+ import isFunction from '@roots/bud-support/isFunction'
6
6
 
7
7
  class Registrable implements BuildBase {
8
8
  /**
package/src/index.ts CHANGED
@@ -49,18 +49,22 @@ declare module '@roots/bud-framework' {
49
49
 
50
50
  interface Rules {
51
51
  [`css-module`]: Rule
52
+ [`inline-font`]: Rule
53
+ [`inline-image`]: Rule
54
+ [`inline-svg`]: Rule
52
55
  css: Rule
53
56
  csv: Rule
54
57
  font: Rule
55
58
  html: Rule
56
59
  image: Rule
57
- inlineFont: Rule
58
- inlineImage: Rule
59
- inlineSvg: Rule
60
60
  js: Rule
61
61
  json: Rule
62
+ sass: Rule
62
63
  svg: Rule
63
64
  toml: Rule
65
+ ts: Rule
66
+ vue: Rule
67
+ webp: Rule
64
68
  yml: Rule
65
69
  }
66
70
  }
package/src/item/index.ts CHANGED
@@ -6,7 +6,8 @@ import {basename} from 'node:path'
6
6
  import Registrable from '@roots/bud-build/helpers/registrable'
7
7
  import Loader from '@roots/bud-build/loader'
8
8
  import {bind} from '@roots/bud-support/decorators/bind'
9
- import isString from '@roots/bud-support/lodash/isString'
9
+ import isString from '@roots/bud-support/isString'
10
+ import logger from '@roots/bud-support/logger'
10
11
 
11
12
  export type ConstructorOptions = Build.Item.ConstructorOptions
12
13
 
@@ -139,7 +140,7 @@ class Item extends Registrable implements Build.Item {
139
140
  }
140
141
 
141
142
  if (!output.loader) {
142
- this.app.error(`error in ${this.ident}`, `no loader registered`)
143
+ logger.error(`error in ${this.ident}`, `no loader registered`)
143
144
  }
144
145
 
145
146
  return Object.entries(output).reduce(
@@ -1,7 +1,7 @@
1
1
  import type {Factory} from '@roots/bud-build/registry'
2
2
  import type {Item} from '@roots/bud-framework/services/build'
3
3
 
4
- import isBoolean from '@roots/bud-support/lodash/isBoolean'
4
+ import isBoolean from '@roots/bud-support/isBoolean'
5
5
 
6
6
  /**
7
7
  * CSS loader
@@ -4,7 +4,7 @@ import type {Bud} from '@roots/bud-framework'
4
4
  import * as items from '@roots/bud-build/items'
5
5
  import * as loaders from '@roots/bud-build/loaders'
6
6
  import * as rules from '@roots/bud-build/rules'
7
- import kebabCase from '@roots/bud-support/lodash/kebabCase'
7
+ import kebabCase from '@roots/bud-support/kebabCase'
8
8
 
9
9
  interface Props {
10
10
  filter: Bud[`hooks`][`filter`]
@@ -36,7 +36,11 @@ async function register(bud: Bud) {
36
36
  ? bud.build.items.minicss
37
37
  : bud.build.items.style
38
38
 
39
- Object.entries(rules).map(makeRegister(bud, bud.build.setRule))
39
+ await Promise.all(
40
+ Object.entries(rules).map(makeRegister(bud, bud.build.setRule)),
41
+ ).catch(e => {
42
+ throw e
43
+ })
40
44
  }
41
45
 
42
46
  /**
package/src/rule/index.ts CHANGED
@@ -9,8 +9,9 @@ import type {
9
9
 
10
10
  import Registrable from '@roots/bud-build/helpers/registrable'
11
11
  import {bind} from '@roots/bud-support/decorators/bind'
12
- import isFunction from '@roots/bud-support/lodash/isFunction'
13
- import isString from '@roots/bud-support/lodash/isString'
12
+ import isFunction from '@roots/bud-support/isFunction'
13
+ import isString from '@roots/bud-support/isString'
14
+ import logger from '@roots/bud-support/logger'
14
15
 
15
16
  /**
16
17
  * RuleSetRule
@@ -31,6 +32,11 @@ class Rule extends Registrable implements Interface {
31
32
  */
32
33
  public include?: Options[`include`]
33
34
 
35
+ /**
36
+ * RuleSetRule issuer
37
+ */
38
+ public issuer?: Options[`issuer`]
39
+
34
40
  /**
35
41
  * RuleSetRule parser
36
42
  */
@@ -104,6 +110,14 @@ class Rule extends Registrable implements Interface {
104
110
  return this.include?.map(this.unwrap)
105
111
  }
106
112
 
113
+ /**
114
+ * Get `issuer` value
115
+ */
116
+ @bind
117
+ public getIssuer(): Output['issuer'] {
118
+ return this.issuer
119
+ }
120
+
107
121
  /**
108
122
  * Get `parser` value
109
123
  */
@@ -184,6 +198,15 @@ class Rule extends Registrable implements Interface {
184
198
  return this
185
199
  }
186
200
 
201
+ /**
202
+ * Set `issuer` value
203
+ */
204
+ @bind
205
+ public setIssuer(issuer: Options['issuer']): this {
206
+ this.issuer = issuer
207
+ return this
208
+ }
209
+
187
210
  /**
188
211
  * Set `parser` value
189
212
  */
@@ -252,6 +275,7 @@ class Rule extends Registrable implements Interface {
252
275
  exclude: this.getExclude(),
253
276
  generator: this.getGenerator(),
254
277
  include: this.getInclude(),
278
+ issuer: this.getIssuer(),
255
279
  parser: this.getParser(),
256
280
  resolve: this.getResolve(),
257
281
  resourceQuery: this.getResourceQuery(),
@@ -273,7 +297,7 @@ class Rule extends Registrable implements Interface {
273
297
  return {...a, [k]: v}
274
298
  }, {})
275
299
 
276
- this.app.info(`built rule`, output)
300
+ logger.info(`built rule`, output)
277
301
 
278
302
  return output
279
303
  }
@@ -10,6 +10,6 @@ const inlineSvg: Factory = async ({filter, makeRule, path}) =>
10
10
  .setGenerator({dataUrl})
11
11
  .setType(`asset/inline`)
12
12
 
13
- const dataUrl = (data: Buffer) => dataUri(data.toString())
13
+ const dataUrl = (data: Uint8Array) => dataUri(data.toString())
14
14
 
15
15
  export {dataUrl, inlineSvg as default}
@@ -1,4 +1,3 @@
1
- import type {Records} from '@roots/bud-build/config'
2
1
  import type {Bud, Build as BudBuild} from '@roots/bud-framework'
3
2
  import type {Items, Loaders, Rules} from '@roots/bud-framework'
4
3
  import type {Configuration} from '@roots/bud-framework/config'
@@ -9,8 +8,8 @@ import {register} from '@roots/bud-build/registry'
9
8
  import Rule, {type Options as RuleOptions} from '@roots/bud-build/rule'
10
9
  import {Service} from '@roots/bud-framework/service'
11
10
  import {bind} from '@roots/bud-support/decorators/bind'
12
- import isFunction from '@roots/bud-support/lodash/isFunction'
13
- import isUndefined from '@roots/bud-support/lodash/isUndefined'
11
+ import isFunction from '@roots/bud-support/isFunction'
12
+ import isUndefined from '@roots/bud-support/isUndefined'
14
13
 
15
14
  /**
16
15
  * {@link BudBuild}
@@ -44,6 +43,7 @@ class Build extends Service implements BudBuild {
44
43
  /**
45
44
  * {@link Service.register}
46
45
  */
46
+ @bind
47
47
  public override async bootstrap?(app: Bud) {
48
48
  this.items = {} as Items
49
49
  this.loaders = {} as Loaders
@@ -92,28 +92,41 @@ class Build extends Service implements BudBuild {
92
92
  this.logger.log(`bud.build.make called`)
93
93
  await this.app.hooks.fire(`build.before`, this.app)
94
94
 
95
- await import(`@roots/bud-build/config`).then(
96
- async (records: Records) =>
97
- await Promise.all(
98
- Object.entries(records).map(async ([prop, factory]) => {
99
- try {
100
- const value = await factory(this.app)
101
- if (isUndefined(value)) return
102
-
103
- this.config[prop] = value
104
- this.logger.success(`built`, prop)
105
- } catch (error) {
106
- throw error
107
- }
108
- }),
109
- ),
110
- )
111
-
112
- this.logger.success(`configuration successfully built`)
95
+ await import(`@roots/bud-build/config`)
96
+ .then(
97
+ async records =>
98
+ await Promise.all(
99
+ Object.entries(records).map(async ([prop, factory]) => {
100
+ const value = await factory(this.app).catch(this.catch)
101
+ if (isUndefined(value)) {
102
+ this.logger.success(`omitting:`, prop, `(undefined)`)
103
+ return
104
+ }
105
+
106
+ Object.defineProperty(this.config, prop, {
107
+ configurable: true,
108
+ enumerable: true,
109
+ value,
110
+ writable: true,
111
+ })
112
+
113
+ this.logger
114
+ .success(`defined:`, prop, `(${typeof this.config[prop]})`)
115
+ .info(prop, `info:`, this.config[prop])
116
+ }),
117
+ ),
118
+ )
119
+ .catch(this.catch)
120
+
121
+ this.logger.success(`configuration built`)
113
122
  this.logger.info(this.config)
114
- await this.app.hooks.fire(`build.after`, this.app)
115
123
 
116
- return this.config
124
+ await this.app.hooks.fire(`build.after`, this.app).catch(this.catch)
125
+
126
+ return Object.entries(this.config).reduce((a, [k, v]) => {
127
+ if (isUndefined(v)) return a
128
+ return {...a, [k]: v}
129
+ }, {})
117
130
  }
118
131
 
119
132
  /**
@@ -148,6 +161,7 @@ class Build extends Service implements BudBuild {
148
161
  ident: K,
149
162
  definition?: ((item: Items[K]) => Items[K]) | Items[K],
150
163
  ): this {
164
+ this.logger.log(`build.setItem`, ident)
151
165
  const maybeOptionsCallback = isUndefined(definition)
152
166
  ? {ident, loader: ident}
153
167
  : definition
@@ -157,7 +171,7 @@ class Build extends Service implements BudBuild {
157
171
  : this.makeItem(maybeOptionsCallback)
158
172
 
159
173
  this.items[ident] = item
160
- this.logger.info(`set item`, item)
174
+ this.logger.info(item)
161
175
 
162
176
  return this
163
177
  }
@@ -170,14 +184,15 @@ class Build extends Service implements BudBuild {
170
184
  name: K,
171
185
  definition?: any,
172
186
  ): this {
187
+ this.logger.log(`build.setLoader`, name)
173
188
  const loader = isUndefined(definition)
174
189
  ? this.makeLoader(name)
175
190
  : definition instanceof Loader
176
- ? definition
177
- : this.makeLoader(definition)
191
+ ? definition
192
+ : this.makeLoader(definition)
178
193
 
179
194
  this.loaders[name] = loader
180
- this.logger.info(`set loader`, loader)
195
+ this.logger.info(loader)
181
196
 
182
197
  return this
183
198
  }
@@ -190,15 +205,16 @@ class Build extends Service implements BudBuild {
190
205
  name: K,
191
206
  definition?: Rule | RuleOptions,
192
207
  ): this {
208
+ this.logger.log(`build.setRule`, name)
193
209
  const rule =
194
210
  definition instanceof Rule
195
211
  ? definition
196
212
  : isFunction(definition)
197
- ? definition(this.makeRule())
198
- : this.makeRule(definition as any)
213
+ ? definition(this.makeRule())
214
+ : this.makeRule(definition as any)
199
215
 
200
216
  this.rules[name] = rule
201
- this.logger.info(`set rule`, rule)
217
+ this.logger.info(rule)
202
218
 
203
219
  return this
204
220
  }