@financial-times/dotcom-build-sass 11.2.1 → 12.0.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 +0 -4
- package/dist/node/index.d.ts +2 -4
- package/dist/node/index.js +18 -36
- package/dist/node/monitored-sass-loader.js +47 -48
- package/dist/tsconfig.tsbuildinfo +1 -2823
- package/package.json +11 -19
- package/src/index.ts +17 -41
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.
|
package/dist/node/index.d.ts
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
import type webpack from 'webpack';
|
2
|
-
export
|
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,
|
13
|
+
constructor({ includePaths, additionalData, webpackImporter, implementation }?: TPluginOptions);
|
16
14
|
apply(compiler: webpack.Compiler): void;
|
17
15
|
}
|
package/dist/node/index.js
CHANGED
@@ -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 = [],
|
11
|
+
constructor({ includePaths = [], additionalData = '', webpackImporter, implementation = 'sass' } = {}) {
|
11
12
|
this.includePaths = includePaths;
|
12
|
-
this.additionalData = additionalData
|
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: ['
|
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
|
-
|
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
|
-
|
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,
|
3
|
-
if (!
|
4
|
-
|
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,
|
9
|
-
if (
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
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
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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,
|
50
|
-
__classPrivateFieldSet(this,
|
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,
|
54
|
-
const updatedTotal = (__classPrivateFieldGet(this,
|
55
|
-
__classPrivateFieldGet(this,
|
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
|
-
|
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,
|
62
|
-
__classPrivateFieldSet(this,
|
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,
|
63
|
+
return __classPrivateFieldGet(this, _SassStats_stats, "f");
|
66
64
|
});
|
67
|
-
|
68
|
-
__classPrivateFieldSet(this,
|
69
|
-
fs_1.default.writeFileSync(__classPrivateFieldGet(this,
|
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,
|
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,
|
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,
|
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,
|
131
|
-
const percentageTotalTimeThrottle = __classPrivateFieldGet(this,
|
132
|
-
(__classPrivateFieldGet(this,
|
133
|
-
|
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,
|
138
|
+
__classPrivateFieldGet(this, _SassStats_report, "f").call(this);
|
140
139
|
}
|
141
140
|
};
|
142
|
-
|
143
|
-
const seconds = __classPrivateFieldGet(this,
|
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,
|
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,
|
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,
|
164
|
+
fs_1.default.mkdirSync(__classPrivateFieldGet(this, _SassStats_directory, "f"), { recursive: true });
|
166
165
|
}
|
167
166
|
}
|
168
|
-
|
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) => {
|