@financial-times/dotcom-build-sass 11.3.0 → 12.0.1

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
@@ -49,8 +49,6 @@ new PageKitSassPlugin({ includePaths: [path.resolve('./path-to-sass-files')] })
49
49
 
50
50
  _Please note_ that by default Sass will resolve all bare `@import` statements from the current working directory rather than relative to the file being processed. This means it will not find dependencies in nested `node_modules` directories.
51
51
 
52
- [PostCSS] is configured with the [Autoprefixer] and [cssnano] transforms.
53
-
54
52
  The CSS loader has `@import` and `url()` resolution disabled as these should be handled by Sass.
55
53
 
56
54
  [rule]: https://webpack.js.org/configuration/module/#rule
@@ -73,8 +71,6 @@ The CSS loader has `@import` and `url()` resolution disabled as these should be
73
71
  | `includePaths` | String[] | `[]` | See https://sass-lang.com/documentation/js-api#includepaths |
74
72
  | `implementation` | `sass\|sass-embedded` | `sass` | See https://webpack.js.org/loaders/sass-loader/#implementation |
75
73
 
76
- `additionalData` replaces `prependData` as of sass-loader v9. `prependData` is still supported in this package, but is deprecated.
77
-
78
74
  ## Sass build monitoring
79
75
 
80
76
  Sass build times are stored locally and remotely, where your project sets relevant API keys. Alternatively, you may turn both these features off using environment variable.
@@ -1,17 +1,15 @@
1
1
  import type webpack from 'webpack';
2
- export declare type TPluginOptions = {
2
+ export type TPluginOptions = {
3
3
  includePaths?: string[];
4
- prependData?: string;
5
4
  additionalData?: string;
6
5
  webpackImporter?: boolean;
7
6
  implementation?: 'sass' | 'sass-embedded';
8
7
  };
9
8
  export declare class PageKitSassPlugin {
10
9
  includePaths: string[];
11
- prependData: string;
12
10
  additionalData: string;
13
11
  webpackImporter: boolean;
14
12
  implementation: 'sass' | 'sass-embedded';
15
- constructor({ includePaths, prependData, additionalData, webpackImporter, implementation }?: TPluginOptions);
13
+ constructor({ includePaths, additionalData, webpackImporter, implementation }?: TPluginOptions);
16
14
  apply(compiler: webpack.Compiler): void;
17
15
  }
@@ -4,16 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PageKitSassPlugin = void 0;
7
- const webpack_fix_style_only_entries_1 = __importDefault(require("webpack-fix-style-only-entries"));
8
7
  const mini_css_extract_plugin_1 = __importDefault(require("mini-css-extract-plugin"));
8
+ const webpack_remove_empty_scripts_1 = __importDefault(require("webpack-remove-empty-scripts"));
9
+ const css_minimizer_webpack_plugin_1 = __importDefault(require("css-minimizer-webpack-plugin"));
9
10
  class PageKitSassPlugin {
10
- constructor({ includePaths = [], prependData = '', additionalData = '', webpackImporter, implementation = 'sass' } = {}) {
11
+ constructor({ includePaths = [], additionalData = '', webpackImporter, implementation = 'sass' } = {}) {
11
12
  this.includePaths = includePaths;
12
- this.additionalData = additionalData.length ? additionalData : prependData;
13
+ this.additionalData = additionalData;
13
14
  this.webpackImporter = webpackImporter;
14
15
  this.implementation = implementation;
15
16
  }
16
17
  apply(compiler) {
18
+ var _a;
17
19
  const sassLoaderOptions = {
18
20
  // This enables the use of enhanced-resolve for @import statements prefixed with ~
19
21
  // but we don't usually use this and disabling it can speed up builds by up to 20%.
@@ -28,39 +30,15 @@ class PageKitSassPlugin {
28
30
  // Disable formatting so that we don't spend time pretty printing
29
31
  outputStyle: 'compressed',
30
32
  // Enable Sass to @import source files from installed dependencies
31
- includePaths: ['bower_components', 'node_modules', ...this.includePaths]
33
+ includePaths: ['node_modules/@financial-times', 'node_modules', ...this.includePaths]
32
34
  }
33
35
  };
34
- const autoprefixerOptions = {
35
- // https://github.com/browserslist/browserslist
36
- overrideBrowserslist: ['last 1 Chrome versions', 'Safari >= 13', 'ff ESR', 'last 1 Edge versions'],
37
- grid: true
38
- };
39
- // https://cssnano.co/guides/optimisations
40
- const cssnanoOptions = {
41
- preset: [
42
- 'default',
43
- {
44
- // disable reduceInitial optimisation as `initial` is not supported in IE11
45
- // https://github.com/cssnano/cssnano/issues/721
46
- // https://developer.mozilla.org/en-US/docs/Web/CSS/initial
47
- reduceInitial: false
48
- }
49
- ]
50
- };
51
36
  const postcssLoaderOptions = {
52
37
  postcssOptions: {
53
38
  plugins: [
54
39
  // Allow @import of CSS files from node_modules
55
40
  // https://github.com/postcss/postcss-import
56
- require('postcss-import')(),
57
- // Add vendor prefixes automatically using data from Can I Use
58
- // https://github.com/postcss/autoprefixer
59
- require('autoprefixer')(autoprefixerOptions),
60
- // Ensure that the final result is as small as possible. This can
61
- // de-duplicate rule-sets which is useful if $o-silent-mode is toggled.
62
- // https://github.com/cssnano/cssnano
63
- require('cssnano')(cssnanoOptions)
41
+ require('postcss-import')()
64
42
  ]
65
43
  },
66
44
  implementation: require('postcss')
@@ -78,12 +56,9 @@ class PageKitSassPlugin {
78
56
  };
79
57
  const miniCssExtractPluginOptions = {
80
58
  // only include content hash in filename when compiling production assets
81
- filename: compiler.options.mode === 'development' ? '[name].css' : '[name].[contenthash:12].css'
82
- };
83
- // This plugin prevents empty JS bundles being created for CSS entry points
84
- // https://github.com/fqborges/webpack-fix-style-only-entries
85
- const stylesOnlyPluginOptions = {
86
- silent: true
59
+ filename: compiler.options.mode === 'development' ? '[name].css' : '[name].[contenthash:12].css',
60
+ // we load CSS files ourselves in `dotcom-ui-shell` so don't need the runtime
61
+ runtime: false
87
62
  };
88
63
  compiler.options.module.rules.push({
89
64
  test: [/\.sass|scss$/],
@@ -113,7 +88,14 @@ class PageKitSassPlugin {
113
88
  }
114
89
  ]
115
90
  });
116
- new webpack_fix_style_only_entries_1.default(stylesOnlyPluginOptions).apply(compiler);
91
+ compiler.options.optimization.minimizer = [
92
+ ...((_a = compiler.options.optimization.minimizer) !== null && _a !== void 0 ? _a : []),
93
+ new css_minimizer_webpack_plugin_1.default()
94
+ ];
95
+ // 2024 and this is still an issue :/ mini-css-extract-plugin leaves
96
+ // behind empty .js bundles after extracting the CSS.
97
+ // https://github.com/webpack/webpack/issues/11671
98
+ new webpack_remove_empty_scripts_1.default().apply(compiler);
117
99
  new mini_css_extract_plugin_1.default(miniCssExtractPluginOptions).apply(compiler);
118
100
  }
119
101
  }
@@ -1,21 +1,19 @@
1
1
  "use strict";
2
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
3
- if (!privateMap.has(receiver)) {
4
- throw new TypeError("attempted to get private field on non-instance");
5
- }
6
- return privateMap.get(receiver);
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
7
6
  };
8
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {
9
- if (!privateMap.has(receiver)) {
10
- throw new TypeError("attempted to set private field on non-instance");
11
- }
12
- privateMap.set(receiver, value);
13
- return value;
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
14
12
  };
15
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
16
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
17
15
  };
18
- var _monitorRemotely, _noticeStrategies, _noticeStrategy, _noticeThrottleSeconds, _noticeThrottlePercentage, _stats, _directory, _file, _startTime, _endTime, _read, _write, _report;
16
+ var _SassStats_monitorRemotely, _SassStats_noticeStrategies, _SassStats_noticeStrategy, _SassStats_noticeThrottleSeconds, _SassStats_noticeThrottlePercentage, _SassStats_stats, _SassStats_directory, _SassStats_file, _SassStats_startTime, _SassStats_endTime, _SassStats_read, _SassStats_write, _SassStats_report;
19
17
  Object.defineProperty(exports, "__esModule", { value: true });
20
18
  const fs_1 = __importDefault(require("fs"));
21
19
  const path_1 = __importDefault(require("path"));
@@ -28,48 +26,48 @@ const logError = (message) => {
28
26
  };
29
27
  class SassStats {
30
28
  constructor() {
31
- _monitorRemotely.set(this, process.env.FT_SASS_STATS_MONITOR === 'on');
32
- _noticeStrategies.set(this, ['throttle', 'never', 'always']);
33
- _noticeStrategy.set(this, __classPrivateFieldGet(this, _noticeStrategies).includes(process.env.FT_SASS_STATS_NOTICE)
29
+ _SassStats_monitorRemotely.set(this, process.env.FT_SASS_STATS_MONITOR === 'on');
30
+ _SassStats_noticeStrategies.set(this, ['throttle', 'never', 'always']);
31
+ _SassStats_noticeStrategy.set(this, __classPrivateFieldGet(this, _SassStats_noticeStrategies, "f").includes(process.env.FT_SASS_STATS_NOTICE)
34
32
  ? process.env.FT_SASS_STATS_NOTICE
35
33
  : 'throttle');
36
- _noticeThrottleSeconds.set(this, typeof process.env.FT_SASS_STATS_NOTICE_THROTTLE_SECONDS === 'number'
34
+ _SassStats_noticeThrottleSeconds.set(this, typeof process.env.FT_SASS_STATS_NOTICE_THROTTLE_SECONDS === 'number'
37
35
  ? process.env.FT_SASS_STATS_NOTICE_THROTTLE_SECONDS
38
36
  : 60 * 60 * 0.5); // show throttled notice given 30 mins since last notice
39
- _noticeThrottlePercentage.set(this, typeof process.env.FT_SASS_STATS_NOTICE_THROTTLE_PERCENTAGE === 'number'
37
+ _SassStats_noticeThrottlePercentage.set(this, typeof process.env.FT_SASS_STATS_NOTICE_THROTTLE_PERCENTAGE === 'number'
40
38
  ? process.env.FT_SASS_STATS_NOTICE_THROTTLE_PERCENTAGE
41
39
  : 30); // show throttled notice given a 30% increase
42
- _stats.set(this, { totalTime: 0, noticeDate: null, totalTimeAtLastNotice: 0 });
43
- _directory.set(this, path_1.default.join(os_1.default.tmpdir(), 'dotcom-build-sass'));
44
- _file.set(this, path_1.default.join(__classPrivateFieldGet(this, _directory), 'sass-stats.json'));
45
- _startTime.set(this, void 0);
46
- _endTime.set(this, void 0);
40
+ _SassStats_stats.set(this, { totalTime: 0, noticeDate: null, totalTimeAtLastNotice: 0 });
41
+ _SassStats_directory.set(this, path_1.default.join(os_1.default.tmpdir(), 'dotcom-build-sass'));
42
+ _SassStats_file.set(this, path_1.default.join(__classPrivateFieldGet(this, _SassStats_directory, "f"), 'sass-stats.json'));
43
+ _SassStats_startTime.set(this, void 0);
44
+ _SassStats_endTime.set(this, void 0);
47
45
  this.buildCount = 0;
48
46
  this.start = () => {
49
- __classPrivateFieldGet(this, _read).call(this);
50
- __classPrivateFieldSet(this, _startTime, performance.now());
47
+ __classPrivateFieldGet(this, _SassStats_read, "f").call(this);
48
+ __classPrivateFieldSet(this, _SassStats_startTime, performance.now(), "f");
51
49
  };
52
50
  this.end = () => {
53
- __classPrivateFieldSet(this, _endTime, performance.now());
54
- const updatedTotal = (__classPrivateFieldGet(this, _stats).totalTime += __classPrivateFieldGet(this, _endTime) - __classPrivateFieldGet(this, _startTime));
55
- __classPrivateFieldGet(this, _write).call(this, { totalTime: updatedTotal });
51
+ __classPrivateFieldSet(this, _SassStats_endTime, performance.now(), "f");
52
+ const updatedTotal = (__classPrivateFieldGet(this, _SassStats_stats, "f").totalTime += __classPrivateFieldGet(this, _SassStats_endTime, "f") - __classPrivateFieldGet(this, _SassStats_startTime, "f"));
53
+ __classPrivateFieldGet(this, _SassStats_write, "f").call(this, { totalTime: updatedTotal });
56
54
  };
57
- _read.set(this, () => {
55
+ _SassStats_read.set(this, () => {
58
56
  try {
59
57
  // Restore stats from a temporary file if it exists.
60
58
  // Reading from disk ensures that we can track stats across builds.
61
- const statsFile = fs_1.default.readFileSync(__classPrivateFieldGet(this, _file), 'utf-8');
62
- __classPrivateFieldSet(this, _stats, JSON.parse(statsFile));
59
+ const statsFile = fs_1.default.readFileSync(__classPrivateFieldGet(this, _SassStats_file, "f"), 'utf-8');
60
+ __classPrivateFieldSet(this, _SassStats_stats, JSON.parse(statsFile), "f");
63
61
  }
64
62
  catch (_a) { }
65
- return __classPrivateFieldGet(this, _stats);
63
+ return __classPrivateFieldGet(this, _SassStats_stats, "f");
66
64
  });
67
- _write.set(this, (stats) => {
68
- __classPrivateFieldSet(this, _stats, Object.assign(__classPrivateFieldGet(this, _stats), stats));
69
- fs_1.default.writeFileSync(__classPrivateFieldGet(this, _file), JSON.stringify(__classPrivateFieldGet(this, _stats)));
65
+ _SassStats_write.set(this, (stats) => {
66
+ __classPrivateFieldSet(this, _SassStats_stats, Object.assign(__classPrivateFieldGet(this, _SassStats_stats, "f"), stats), "f");
67
+ fs_1.default.writeFileSync(__classPrivateFieldGet(this, _SassStats_file, "f"), JSON.stringify(__classPrivateFieldGet(this, _SassStats_stats, "f")));
70
68
  });
71
69
  this.sendMetric = () => {
72
- if (!__classPrivateFieldGet(this, _monitorRemotely)) {
70
+ if (!__classPrivateFieldGet(this, _SassStats_monitorRemotely, "f")) {
73
71
  return;
74
72
  }
75
73
  if (!process.env.FT_SASS_BIZ_OPS_API_KEY) {
@@ -84,7 +82,7 @@ class SassStats {
84
82
  const postData = JSON.stringify({
85
83
  type: 'System',
86
84
  metric: 'sass-build-time',
87
- value: (__classPrivateFieldGet(this, _endTime) - __classPrivateFieldGet(this, _startTime)) / 1000,
85
+ value: (__classPrivateFieldGet(this, _SassStats_endTime, "f") - __classPrivateFieldGet(this, _SassStats_startTime, "f")) / 1000,
88
86
  date: date.toISOString(),
89
87
  code: process.env.FT_SASS_BIZ_OPS_SYSTEM_CODE,
90
88
  metadata: {
@@ -117,7 +115,7 @@ class SassStats {
117
115
  };
118
116
  this.reportAccordingToNoticeStrategy = () => {
119
117
  let shouldReport;
120
- switch (__classPrivateFieldGet(this, _noticeStrategy)) {
118
+ switch (__classPrivateFieldGet(this, _SassStats_noticeStrategy, "f")) {
121
119
  case 'never':
122
120
  shouldReport = false;
123
121
  break;
@@ -127,20 +125,21 @@ class SassStats {
127
125
  case 'throttle':
128
126
  // Throttle notices to show a limited number per hour, or if the total sass build time
129
127
  // has increased by a significant percentage. This favours more frequent reports to begin with.
130
- const noticeTimeThrottle = Date.now() >= __classPrivateFieldGet(this, _stats).noticeDate + __classPrivateFieldGet(this, _noticeThrottleSeconds) * 1000;
131
- const percentageTotalTimeThrottle = __classPrivateFieldGet(this, _stats).totalTime > 0 &&
132
- (__classPrivateFieldGet(this, _stats).totalTime / __classPrivateFieldGet(this, _stats).totalTimeAtLastNotice - 1) * 100 >= __classPrivateFieldGet(this, _noticeThrottlePercentage); // % increase
133
- shouldReport = !__classPrivateFieldGet(this, _stats).noticeDate || noticeTimeThrottle || percentageTotalTimeThrottle;
128
+ const noticeTimeThrottle = Date.now() >= __classPrivateFieldGet(this, _SassStats_stats, "f").noticeDate + __classPrivateFieldGet(this, _SassStats_noticeThrottleSeconds, "f") * 1000;
129
+ const percentageTotalTimeThrottle = __classPrivateFieldGet(this, _SassStats_stats, "f").totalTime > 0 &&
130
+ (__classPrivateFieldGet(this, _SassStats_stats, "f").totalTime / __classPrivateFieldGet(this, _SassStats_stats, "f").totalTimeAtLastNotice - 1) * 100 >=
131
+ __classPrivateFieldGet(this, _SassStats_noticeThrottlePercentage, "f"); // % increase
132
+ shouldReport = !__classPrivateFieldGet(this, _SassStats_stats, "f").noticeDate || noticeTimeThrottle || percentageTotalTimeThrottle;
134
133
  break;
135
134
  default:
136
135
  break;
137
136
  }
138
137
  if (shouldReport) {
139
- __classPrivateFieldGet(this, _report).call(this);
138
+ __classPrivateFieldGet(this, _SassStats_report, "f").call(this);
140
139
  }
141
140
  };
142
- _report.set(this, () => {
143
- const seconds = __classPrivateFieldGet(this, _stats).totalTime / 1000;
141
+ _SassStats_report.set(this, () => {
142
+ const seconds = __classPrivateFieldGet(this, _SassStats_stats, "f").totalTime / 1000;
144
143
  const minutes = seconds / 60;
145
144
  const hours = seconds / 3600;
146
145
  const time = hours > 1
@@ -151,7 +150,7 @@ class SassStats {
151
150
  const emoji = hours > 2 ? ['🔥', '😭', '😱'] : hours >= 1 ? ['🔥', '😱'] : minutes > 10 ? ['⏱️', '😬'] : ['⏱️'];
152
151
  let cta = `Share your high score in Slack #sass-to-css 🎉 And help us improve that:\n` +
153
152
  `https://origami.ft.com/blog/2024/01/24/sass-build-times/\n\n`;
154
- if (!__classPrivateFieldGet(this, _monitorRemotely)) {
153
+ if (!__classPrivateFieldGet(this, _SassStats_monitorRemotely, "f")) {
155
154
  cta =
156
155
  `Help us improve build times by setting the "FT_SASS_STATS_MONITOR" environment variable.\n` +
157
156
  `https://origami.ft.com/blog/2024/01/24/sass-build-times/ \n\n`;
@@ -160,12 +159,12 @@ class SassStats {
160
159
  console.log(`\n\ndotcom-build-sass:\nYou have spent at least ${emoji.join(' ')} ${time} ${emoji
161
160
  .reverse()
162
161
  .join(' ')} waiting on FT Sass to compile.\n${cta}`);
163
- __classPrivateFieldGet(this, _write).call(this, { noticeDate: Date.now(), totalTimeAtLastNotice: __classPrivateFieldGet(this, _stats).totalTime });
162
+ __classPrivateFieldGet(this, _SassStats_write, "f").call(this, { noticeDate: Date.now(), totalTimeAtLastNotice: __classPrivateFieldGet(this, _SassStats_stats, "f").totalTime });
164
163
  });
165
- fs_1.default.mkdirSync(__classPrivateFieldGet(this, _directory), { recursive: true });
164
+ fs_1.default.mkdirSync(__classPrivateFieldGet(this, _SassStats_directory, "f"), { recursive: true });
166
165
  }
167
166
  }
168
- _monitorRemotely = new WeakMap(), _noticeStrategies = new WeakMap(), _noticeStrategy = new WeakMap(), _noticeThrottleSeconds = new WeakMap(), _noticeThrottlePercentage = new WeakMap(), _stats = new WeakMap(), _directory = new WeakMap(), _file = new WeakMap(), _startTime = new WeakMap(), _endTime = new WeakMap(), _read = new WeakMap(), _write = new WeakMap(), _report = new WeakMap();
167
+ _SassStats_monitorRemotely = new WeakMap(), _SassStats_noticeStrategies = new WeakMap(), _SassStats_noticeStrategy = new WeakMap(), _SassStats_noticeThrottleSeconds = new WeakMap(), _SassStats_noticeThrottlePercentage = new WeakMap(), _SassStats_stats = new WeakMap(), _SassStats_directory = new WeakMap(), _SassStats_file = new WeakMap(), _SassStats_startTime = new WeakMap(), _SassStats_endTime = new WeakMap(), _SassStats_read = new WeakMap(), _SassStats_write = new WeakMap(), _SassStats_report = new WeakMap();
169
168
  // We're proxying a few functions for monitoring purposes,
170
169
  // we want to catch any monitoring errors silently.
171
170
  const forgivingProxy = (target, task) => {