sku 12.6.2 → 12.8.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/CHANGELOG.md +81 -0
- package/config/babel/babelConfig.js +7 -0
- package/config/webpack/webpack.config.js +12 -1
- package/config/webpack/webpack.config.ssr.js +12 -1
- package/entry/csp.js +42 -16
- package/entry/csp.test.js +93 -0
- package/entry/makeExtractor.js +6 -0
- package/entry/server/server.js +2 -0
- package/lib/parseArgs.js +1 -0
- package/package.json +10 -8
- package/scripts/translations.js +11 -7
- package/sku-types.d.ts +27 -0
- package/telemetry/index.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
# sku
|
|
2
2
|
|
|
3
|
+
## 12.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Export a `Server` type for `sku`'s server entrypoint ([#981](https://github.com/seek-oss/sku/pull/981))
|
|
8
|
+
|
|
9
|
+
**EXAMPLE USAGE**:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// server.tsx
|
|
13
|
+
import { renderToString } from 'react-dom/server';
|
|
14
|
+
import type { Server } from 'sku';
|
|
15
|
+
import { App } from './App';
|
|
16
|
+
|
|
17
|
+
export default (): Server => ({
|
|
18
|
+
renderCallback: ({ SkuProvider, getHeadTags, getBodyTags }, _req, res) => {
|
|
19
|
+
const app = renderToString(
|
|
20
|
+
<SkuProvider>
|
|
21
|
+
<App />
|
|
22
|
+
</SkuProvider>,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
res.send(/* html */ `
|
|
26
|
+
<!DOCTYPE html>
|
|
27
|
+
<html>
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="UTF-8">
|
|
30
|
+
<title>My Awesome Project</title>
|
|
31
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
32
|
+
${getHeadTags()}
|
|
33
|
+
</head>
|
|
34
|
+
<body>
|
|
35
|
+
<div id="app">${app}</div>
|
|
36
|
+
${getBodyTags()}
|
|
37
|
+
</body>
|
|
38
|
+
</html>`);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> [!NOTE]
|
|
44
|
+
> The `Server` type may conflict with existing attempts in projects to define a `Server` type.
|
|
45
|
+
|
|
46
|
+
## 12.7.0
|
|
47
|
+
|
|
48
|
+
### Minor Changes
|
|
49
|
+
|
|
50
|
+
- Update TypeScript from 5.3 to 5.5 ([#1003](https://github.com/seek-oss/sku/pull/1003))
|
|
51
|
+
|
|
52
|
+
Both the 5.4 and 5.5 releases include breaking changes. See the [TypeScript 5.4 announcement] and [TypeScript 5.5 announcement] for more information.
|
|
53
|
+
|
|
54
|
+
[typescript 5.4 announcement]: https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/
|
|
55
|
+
[typeScript 5.5 announcement]: https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/
|
|
56
|
+
|
|
57
|
+
- Add support for `--watch` flag to `sku translations compile` ([#989](https://github.com/seek-oss/sku/pull/989))
|
|
58
|
+
|
|
59
|
+
The `sku translations compile` command now accepts a `--watch` flag. When this flag is provided, `translations.json` files will be re-compiled whenever changes are detected.
|
|
60
|
+
|
|
61
|
+
**EXAMPLE USAGE**:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
sku translations compile --watch
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Patch Changes
|
|
68
|
+
|
|
69
|
+
- Update all `@vocab/*` dependencies ([#989](https://github.com/seek-oss/sku/pull/989))
|
|
70
|
+
|
|
71
|
+
- Fixes a bug where the project name was not being reported correctly when sending telemetry ([#1001](https://github.com/seek-oss/sku/pull/1001))
|
|
72
|
+
|
|
73
|
+
- Enable `babel-loader` cache ([#990](https://github.com/seek-oss/sku/pull/990))
|
|
74
|
+
|
|
75
|
+
Sku's webpack configuration now configures `babel-loader` to emit a cache to `node_modules/.cache/babel-loader`. The primary use case for this cache is speeding up production builds. It can also speed up local development in situations where the webpack cache is invalidated.
|
|
76
|
+
|
|
77
|
+
- Minify build output with [SWC] ([#992](https://github.com/seek-oss/sku/pull/992))
|
|
78
|
+
|
|
79
|
+
Minification of production build output is now performed by [SWC]. Previously this was performed by [Terser]. This should result in a noticeable reduction in build times for larger projects, as well as a slight decrease in bundle size.
|
|
80
|
+
|
|
81
|
+
[swc]: https://swc.rs/docs/configuration/minification
|
|
82
|
+
[terser]: https://terser.org/
|
|
83
|
+
|
|
3
84
|
## 12.6.2
|
|
4
85
|
|
|
5
86
|
### Patch Changes
|
|
@@ -94,6 +94,13 @@ module.exports = ({
|
|
|
94
94
|
return {
|
|
95
95
|
babelrc: false,
|
|
96
96
|
sourceType: isBrowser ? 'unambiguous' : 'module',
|
|
97
|
+
// `babel-jest` does not support the `cacheDirectory` option.
|
|
98
|
+
// It is only used by `babel-loader`.
|
|
99
|
+
...(!isJest
|
|
100
|
+
? {
|
|
101
|
+
cacheDirectory: true,
|
|
102
|
+
}
|
|
103
|
+
: {}),
|
|
97
104
|
presets,
|
|
98
105
|
plugins,
|
|
99
106
|
};
|
|
@@ -134,7 +134,17 @@ const makeWebpackConfig = ({
|
|
|
134
134
|
// The 'TerserPlugin' is actually the default minimizer for webpack
|
|
135
135
|
// We add a custom one to ensure licence comments stay inside the final JS assets
|
|
136
136
|
// Without this a '*.js.LICENSE.txt' file would be created alongside
|
|
137
|
-
minimizer: [
|
|
137
|
+
minimizer: [
|
|
138
|
+
new TerserPlugin({
|
|
139
|
+
extractComments: false,
|
|
140
|
+
minify: TerserPlugin.swcMinify,
|
|
141
|
+
parallel: true,
|
|
142
|
+
terserOptions: {
|
|
143
|
+
compress: true,
|
|
144
|
+
mangle: true,
|
|
145
|
+
},
|
|
146
|
+
}),
|
|
147
|
+
],
|
|
138
148
|
concatenateModules: isProductionBuild,
|
|
139
149
|
...(!isLibrary
|
|
140
150
|
? {
|
|
@@ -176,6 +186,7 @@ const makeWebpackConfig = ({
|
|
|
176
186
|
loader: require.resolve('babel-loader'),
|
|
177
187
|
options: {
|
|
178
188
|
babelrc: false,
|
|
189
|
+
cacheDirectory: true,
|
|
179
190
|
presets: [
|
|
180
191
|
[
|
|
181
192
|
require.resolve('@babel/preset-env'),
|
|
@@ -97,7 +97,17 @@ const makeWebpackConfig = ({
|
|
|
97
97
|
// The 'TerserPlugin' is actually the default minimizer for webpack
|
|
98
98
|
// We add a custom one to ensure licence comments stay inside the final JS assets
|
|
99
99
|
// Without this a '*.js.LICENSE.txt' file would be created alongside
|
|
100
|
-
minimizer: [
|
|
100
|
+
minimizer: [
|
|
101
|
+
new TerserPlugin({
|
|
102
|
+
extractComments: false,
|
|
103
|
+
minify: TerserPlugin.swcMinify,
|
|
104
|
+
parallel: true,
|
|
105
|
+
terserOptions: {
|
|
106
|
+
compress: true,
|
|
107
|
+
mangle: true,
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
101
111
|
concatenateModules: isProductionBuild,
|
|
102
112
|
splitChunks: {
|
|
103
113
|
chunks: 'all',
|
|
@@ -138,6 +148,7 @@ const makeWebpackConfig = ({
|
|
|
138
148
|
loader: require.resolve('babel-loader'),
|
|
139
149
|
options: {
|
|
140
150
|
babelrc: false,
|
|
151
|
+
cacheDirectory: true,
|
|
141
152
|
presets: [
|
|
142
153
|
[
|
|
143
154
|
require.resolve('@babel/preset-env'),
|
package/entry/csp.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
import { createHash } from 'node:crypto';
|
|
2
3
|
import { parse, valid } from 'node-html-parser';
|
|
3
4
|
import { URL } from 'node:url';
|
|
@@ -6,9 +7,18 @@ const scriptTypeIgnoreList = ['application/json', 'application/ld+json'];
|
|
|
6
7
|
|
|
7
8
|
const defaultBaseName = 'http://relative-url';
|
|
8
9
|
|
|
10
|
+
/** @typedef {import("node:crypto").BinaryLike} BinaryLike */
|
|
11
|
+
|
|
12
|
+
/** @param {BinaryLike} scriptContents */
|
|
9
13
|
const hashScriptContents = (scriptContents) =>
|
|
10
14
|
createHash('sha256').update(scriptContents).digest('base64');
|
|
11
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {object} CreateCSPHandlerOptions
|
|
18
|
+
* @property {string[]} [extraHosts=[]]
|
|
19
|
+
* @property {boolean} [isDevelopment=false]
|
|
20
|
+
* @param {CreateCSPHandlerOptions} [options={}]
|
|
21
|
+
*/
|
|
12
22
|
export default function createCSPHandler({
|
|
13
23
|
extraHosts = [],
|
|
14
24
|
isDevelopment = false,
|
|
@@ -17,10 +27,14 @@ export default function createCSPHandler({
|
|
|
17
27
|
const hosts = new Set();
|
|
18
28
|
const shas = new Set();
|
|
19
29
|
|
|
30
|
+
/** @param {BinaryLike | undefined?} contents */
|
|
20
31
|
const addScriptContents = (contents) => {
|
|
21
|
-
|
|
32
|
+
if (contents) {
|
|
33
|
+
shas.add(hashScriptContents(contents));
|
|
34
|
+
}
|
|
22
35
|
};
|
|
23
36
|
|
|
37
|
+
/** @param {string} src */
|
|
24
38
|
const addScriptUrl = (src) => {
|
|
25
39
|
const { origin } = new URL(src, defaultBaseName);
|
|
26
40
|
|
|
@@ -31,18 +45,22 @@ export default function createCSPHandler({
|
|
|
31
45
|
|
|
32
46
|
extraHosts.forEach((host) => addScriptUrl(host));
|
|
33
47
|
|
|
48
|
+
/** @param {import("node-html-parser").HTMLElement} scriptNode */
|
|
34
49
|
const processScriptNode = (scriptNode) => {
|
|
35
50
|
const src = scriptNode.getAttribute('src');
|
|
36
51
|
|
|
37
52
|
if (src) {
|
|
38
53
|
addScriptUrl(src);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const scriptType = scriptNode.getAttribute('type');
|
|
58
|
+
if (scriptType == null || !scriptTypeIgnoreList.includes(scriptType)) {
|
|
59
|
+
addScriptContents(scriptNode.firstChild?.rawText);
|
|
43
60
|
}
|
|
44
61
|
};
|
|
45
62
|
|
|
63
|
+
/** @type {import("../sku-types.d.ts").RenderCallbackParams['registerScript']} */
|
|
46
64
|
const registerScript = (script) => {
|
|
47
65
|
if (tagReturned) {
|
|
48
66
|
throw new Error(
|
|
@@ -53,18 +71,16 @@ export default function createCSPHandler({
|
|
|
53
71
|
);
|
|
54
72
|
}
|
|
55
73
|
|
|
56
|
-
if (
|
|
57
|
-
process.env.NODE_ENV !== 'production' &&
|
|
58
|
-
!valid(script, { script: true })
|
|
59
|
-
) {
|
|
74
|
+
if (process.env.NODE_ENV !== 'production' && !valid(script)) {
|
|
60
75
|
console.error(`Invalid script passed to 'registerScript'\n${script}`);
|
|
61
76
|
}
|
|
62
77
|
|
|
63
|
-
parse(script
|
|
64
|
-
.querySelectorAll('script')
|
|
65
|
-
.forEach(processScriptNode);
|
|
78
|
+
parse(script).querySelectorAll('script').forEach(processScriptNode);
|
|
66
79
|
};
|
|
67
80
|
|
|
81
|
+
/**
|
|
82
|
+
* @returns {string}
|
|
83
|
+
*/
|
|
68
84
|
const createCSPTag = () => {
|
|
69
85
|
tagReturned = true;
|
|
70
86
|
|
|
@@ -90,11 +106,12 @@ export default function createCSPHandler({
|
|
|
90
106
|
)};">`;
|
|
91
107
|
};
|
|
92
108
|
|
|
109
|
+
/**
|
|
110
|
+
* @param {string} html
|
|
111
|
+
* @returns {string}
|
|
112
|
+
*/
|
|
93
113
|
const handleHtml = (html) => {
|
|
94
114
|
const root = parse(html, {
|
|
95
|
-
script: true,
|
|
96
|
-
style: true,
|
|
97
|
-
pre: true,
|
|
98
115
|
comment: true,
|
|
99
116
|
});
|
|
100
117
|
|
|
@@ -108,7 +125,16 @@ export default function createCSPHandler({
|
|
|
108
125
|
|
|
109
126
|
root.querySelectorAll('script').forEach(processScriptNode);
|
|
110
127
|
|
|
111
|
-
root.querySelector('head')
|
|
128
|
+
const headElement = root.querySelector('head');
|
|
129
|
+
if (!headElement) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Unable to find 'head' element in HTML in order to create CSP tag. Check the following output of renderDocument for invalid HTML.\n${
|
|
132
|
+
html.length > 250 ? `${html.substring(0, 200)}...` : html
|
|
133
|
+
}`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
headElement.insertAdjacentHTML('afterbegin', createCSPTag());
|
|
112
138
|
|
|
113
139
|
return root.toString();
|
|
114
140
|
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import createCSPHandler from './csp.js';
|
|
2
|
+
|
|
3
|
+
describe('createCSPHandler', () => {
|
|
4
|
+
it('should create a CSP tag', () => {
|
|
5
|
+
const cspHandler = createCSPHandler();
|
|
6
|
+
|
|
7
|
+
cspHandler.registerScript('<script>console.log("Hello, World!")</script>');
|
|
8
|
+
cspHandler.registerScript(
|
|
9
|
+
'<script><script src="https://code.jquery.com/jquery-3.5.0.slim.min.js"></script></script>',
|
|
10
|
+
);
|
|
11
|
+
cspHandler.registerScript('<script>console.log("Hello, World!")</script>');
|
|
12
|
+
|
|
13
|
+
expect(cspHandler.createCSPTag()).toMatchInlineSnapshot(
|
|
14
|
+
`"<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-uCWFWr3Z6MtMWnxQ5Qyr6+zW+EGpZvdwyqLxlDbe/rE=' 'sha256-lLONtxmGZgnK0e52sTdx8mwK2vMdmln9LPZAbPGHAR0=';">"`,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should inject a CSP tag into HTML', () => {
|
|
19
|
+
const cspHandler = createCSPHandler();
|
|
20
|
+
|
|
21
|
+
cspHandler.registerScript('<script>console.log("Hello, World!")</script>');
|
|
22
|
+
cspHandler.registerScript(
|
|
23
|
+
'<script><script src="https://code.jquery.com/jquery-3.5.0.slim.min.js"></script></script>',
|
|
24
|
+
);
|
|
25
|
+
cspHandler.registerScript('<script>console.log("Hello, World!")</script>');
|
|
26
|
+
|
|
27
|
+
const html =
|
|
28
|
+
/* html */
|
|
29
|
+
`<html>
|
|
30
|
+
<!--
|
|
31
|
+
Hello
|
|
32
|
+
World!
|
|
33
|
+
-->
|
|
34
|
+
<head>
|
|
35
|
+
<script>
|
|
36
|
+
console.log("Hello, World!");
|
|
37
|
+
console.log("Hello, World!");
|
|
38
|
+
</script>
|
|
39
|
+
<noscript>
|
|
40
|
+
You have
|
|
41
|
+
Javascript disabled
|
|
42
|
+
</noscript>
|
|
43
|
+
<style>
|
|
44
|
+
div {
|
|
45
|
+
color: papayawhip;
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<div>
|
|
51
|
+
Welcome to my App!
|
|
52
|
+
</div>
|
|
53
|
+
<pre>
|
|
54
|
+
Multi
|
|
55
|
+
Line
|
|
56
|
+
Text
|
|
57
|
+
</pre>
|
|
58
|
+
</html>`;
|
|
59
|
+
|
|
60
|
+
expect(cspHandler.handleHtml(html)).toMatchInlineSnapshot(`
|
|
61
|
+
"<html>
|
|
62
|
+
<!--
|
|
63
|
+
Hello
|
|
64
|
+
World!
|
|
65
|
+
-->
|
|
66
|
+
<head><meta http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-uCWFWr3Z6MtMWnxQ5Qyr6+zW+EGpZvdwyqLxlDbe/rE=' 'sha256-lLONtxmGZgnK0e52sTdx8mwK2vMdmln9LPZAbPGHAR0=' 'sha256-IYf6lwpasx2aXpcaX33x4ihyqfUb7LQFFjpVwJyfoYk=';">
|
|
67
|
+
<script>
|
|
68
|
+
console.log("Hello, World!");
|
|
69
|
+
console.log("Hello, World!");
|
|
70
|
+
</script>
|
|
71
|
+
<noscript>
|
|
72
|
+
You have
|
|
73
|
+
Javascript disabled
|
|
74
|
+
</noscript>
|
|
75
|
+
<style>
|
|
76
|
+
div {
|
|
77
|
+
color: papayawhip;
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
80
|
+
</head>
|
|
81
|
+
|
|
82
|
+
<div>
|
|
83
|
+
Welcome to my App!
|
|
84
|
+
</div>
|
|
85
|
+
<pre>
|
|
86
|
+
Multi
|
|
87
|
+
Line
|
|
88
|
+
Text
|
|
89
|
+
</pre>
|
|
90
|
+
</html>"
|
|
91
|
+
`);
|
|
92
|
+
});
|
|
93
|
+
});
|
package/entry/makeExtractor.js
CHANGED
|
@@ -10,12 +10,15 @@ const getNewTags = ({ before, after }) => {
|
|
|
10
10
|
return afterArr.filter((tag) => !beforeArr.includes(tag)).join('\n');
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
/** @typedef {import("../sku-types.d.ts").RenderCallbackParams} RenderCallbackParams */
|
|
14
|
+
|
|
13
15
|
export default (stats, publicPath, csp) => {
|
|
14
16
|
const extractor = new ChunkExtractor({
|
|
15
17
|
stats,
|
|
16
18
|
entrypoints: [defaultEntryPoint],
|
|
17
19
|
});
|
|
18
20
|
|
|
21
|
+
/** @type {RenderCallbackParams['SkuProvider']} */
|
|
19
22
|
const SkuProvider = ({ children }) => (
|
|
20
23
|
<ChunkExtractorManager extractor={extractor}>
|
|
21
24
|
{children}
|
|
@@ -40,6 +43,7 @@ export default (stats, publicPath, csp) => {
|
|
|
40
43
|
const getCssHeadTags = () => extractor.getStyleTags();
|
|
41
44
|
|
|
42
45
|
return {
|
|
46
|
+
/** @type RenderCallbackParams['getHeadTags'] */
|
|
43
47
|
getHeadTags: ({ excludeJs, excludeCss } = {}) => {
|
|
44
48
|
const tags = [];
|
|
45
49
|
|
|
@@ -56,6 +60,7 @@ export default (stats, publicPath, csp) => {
|
|
|
56
60
|
}
|
|
57
61
|
return tags.join('\n');
|
|
58
62
|
},
|
|
63
|
+
/** @type RenderCallbackParams['flushHeadTags'] */
|
|
59
64
|
flushHeadTags: ({ excludeJs, excludeCss } = {}) => {
|
|
60
65
|
const tags = [];
|
|
61
66
|
|
|
@@ -92,6 +97,7 @@ export default (stats, publicPath, csp) => {
|
|
|
92
97
|
}
|
|
93
98
|
return tags.join('\n');
|
|
94
99
|
},
|
|
100
|
+
/** @type RenderCallbackParams['getBodyTags'] */
|
|
95
101
|
getBodyTags: () => extractor.getScriptTags(extraScriptTagAttributes),
|
|
96
102
|
SkuProvider,
|
|
97
103
|
extractor,
|
package/entry/server/server.js
CHANGED
|
@@ -54,9 +54,11 @@ app.get('*', (...args) => {
|
|
|
54
54
|
|
|
55
55
|
const { SkuProvider, extractor, flushHeadTags, getHeadTags, getBodyTags } =
|
|
56
56
|
makeExtractor(webpackStats, publicPath, cspHandler);
|
|
57
|
+
/** @type {import("../../sku-types.d.ts").RenderCallbackParams['addLanguageChunk']} */
|
|
57
58
|
const addLanguageChunk = (language) =>
|
|
58
59
|
extractor.addChunk(getChunkName(language));
|
|
59
60
|
|
|
61
|
+
/** @type {import('express').Express} */
|
|
60
62
|
const result = renderCallback(
|
|
61
63
|
{
|
|
62
64
|
SkuProvider,
|
package/lib/parseArgs.js
CHANGED
|
@@ -44,6 +44,7 @@ module.exports = (processArgv) => {
|
|
|
44
44
|
'debug',
|
|
45
45
|
// Passed to Vocab in the `translations` script
|
|
46
46
|
'delete-unused-keys',
|
|
47
|
+
'watch',
|
|
47
48
|
],
|
|
48
49
|
// `minimist` does not push unknown flags to `_` even if this function returns `true`, so we
|
|
49
50
|
// need to track them ourselves
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sku",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.8.0",
|
|
4
4
|
"description": "Front-end development toolkit, powered by Webpack, Babel, CSS Modules, Less and Jest",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -44,14 +44,18 @@
|
|
|
44
44
|
"@storybook/cli": "^7.0.17",
|
|
45
45
|
"@storybook/react": "^7.0.17",
|
|
46
46
|
"@storybook/react-webpack5": "^7.0.17",
|
|
47
|
+
"@types/express": "^4.17.11",
|
|
48
|
+
"@swc/core": "^1.5.25",
|
|
47
49
|
"@types/jest": "^29.0.0",
|
|
48
50
|
"@types/loadable__component": "^5.13.1",
|
|
51
|
+
"@types/loadable__server": "^5.12.10",
|
|
52
|
+
"@types/react": "^18.2.3",
|
|
49
53
|
"@vanilla-extract/jest-transform": "^1.1.0",
|
|
50
54
|
"@vanilla-extract/webpack-plugin": "^2.2.0",
|
|
51
|
-
"@vocab/core": "^1.
|
|
52
|
-
"@vocab/phrase": "^
|
|
55
|
+
"@vocab/core": "^1.6.2",
|
|
56
|
+
"@vocab/phrase": "^2.0.0",
|
|
53
57
|
"@vocab/pseudo-localize": "^1.0.1",
|
|
54
|
-
"@vocab/webpack": "^1.2.
|
|
58
|
+
"@vocab/webpack": "^1.2.9",
|
|
55
59
|
"@zendesk/babel-plugin-react-displayname": "zendesk/babel-plugin-react-displayname#7a837f2",
|
|
56
60
|
"autoprefixer": "^10.3.1",
|
|
57
61
|
"babel-jest": "^29.0.0",
|
|
@@ -114,7 +118,7 @@
|
|
|
114
118
|
"svgo-loader": "^4.0.0",
|
|
115
119
|
"terser-webpack-plugin": "^5.1.4",
|
|
116
120
|
"tree-kill": "^1.2.1",
|
|
117
|
-
"typescript": "~5.
|
|
121
|
+
"typescript": "~5.5.0",
|
|
118
122
|
"webpack": "^5.52.0",
|
|
119
123
|
"webpack-bundle-analyzer": "^4.6.1",
|
|
120
124
|
"webpack-dev-server": "^5.0.2",
|
|
@@ -127,14 +131,12 @@
|
|
|
127
131
|
"devDependencies": {
|
|
128
132
|
"@types/cross-spawn": "^6.0.3",
|
|
129
133
|
"@types/debug": "^4.1.12",
|
|
130
|
-
"@types/express": "^4.17.11",
|
|
131
134
|
"@types/minimist": "^1.2.5",
|
|
132
135
|
"@types/picomatch": "^2.3.3",
|
|
133
|
-
"@types/react": "^18.2.3",
|
|
134
136
|
"@types/react-dom": "^18.2.3",
|
|
135
137
|
"@types/which": "^3.0.0",
|
|
136
138
|
"@vanilla-extract/css": "^1.0.0",
|
|
137
|
-
"@vocab/react": "^1.
|
|
139
|
+
"@vocab/react": "^1.1.11",
|
|
138
140
|
"braid-design-system": "^32.0.0",
|
|
139
141
|
"react": "^18.2.0",
|
|
140
142
|
"react-dom": "^18.2.0",
|
package/scripts/translations.js
CHANGED
|
@@ -2,7 +2,11 @@ const envCi = require('env-ci');
|
|
|
2
2
|
|
|
3
3
|
const { branch } = envCi();
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
-
const
|
|
5
|
+
const {
|
|
6
|
+
argv: args,
|
|
7
|
+
watch,
|
|
8
|
+
'delete-unused-keys': deleteUnusedKeys,
|
|
9
|
+
} = require('../config/args');
|
|
6
10
|
const { compile, validate } = require('@vocab/core');
|
|
7
11
|
const { push, pull } = require('@vocab/phrase');
|
|
8
12
|
const { getVocabConfig } = require('../config/vocab/vocab');
|
|
@@ -51,16 +55,13 @@ const ensureBranch = () => {
|
|
|
51
55
|
|
|
52
56
|
try {
|
|
53
57
|
if (translationSubCommand === 'compile') {
|
|
54
|
-
|
|
58
|
+
console.log('Watching for changes to translations');
|
|
59
|
+
compile({ watch }, vocabConfig);
|
|
55
60
|
}
|
|
56
61
|
if (translationSubCommand === 'validate') {
|
|
57
62
|
validate(vocabConfig);
|
|
58
63
|
}
|
|
59
64
|
if (translationSubCommand === 'push') {
|
|
60
|
-
const deleteUnusedKeys = commandArguments.includes(
|
|
61
|
-
'--delete-unused-keys',
|
|
62
|
-
);
|
|
63
|
-
|
|
64
65
|
ensureBranch();
|
|
65
66
|
push({ branch, deleteUnusedKeys }, vocabConfig);
|
|
66
67
|
}
|
|
@@ -75,5 +76,8 @@ const ensureBranch = () => {
|
|
|
75
76
|
|
|
76
77
|
process.exit(1);
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
+
|
|
80
|
+
if (!watch) {
|
|
81
|
+
console.log(chalk.cyan('Translations complete'));
|
|
82
|
+
}
|
|
79
83
|
})();
|
package/sku-types.d.ts
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import type { Express, RequestHandler } from 'express';
|
|
3
|
+
import type { ChunkExtractor } from '@loadable/server';
|
|
4
|
+
|
|
5
|
+
export interface RenderCallbackParams {
|
|
6
|
+
SkuProvider: ({ children }: { children: ReactNode }) => JSX.Element;
|
|
7
|
+
addLanguageChunk: (language: string) => void;
|
|
8
|
+
getBodyTags: () => string;
|
|
9
|
+
getHeadTags: (options?: {
|
|
10
|
+
excludeJs?: boolean;
|
|
11
|
+
excludeCss?: boolean;
|
|
12
|
+
}) => string;
|
|
13
|
+
flushHeadTags: (options?: {
|
|
14
|
+
excludeJs?: boolean;
|
|
15
|
+
excludeCss?: boolean;
|
|
16
|
+
}) => string;
|
|
17
|
+
extractor: ChunkExtractor;
|
|
18
|
+
registerScript?: (script: string) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Server {
|
|
22
|
+
renderCallback: (
|
|
23
|
+
params: RenderCallbackParams,
|
|
24
|
+
...requestHandlerParams: Parameters<RequestHandler>
|
|
25
|
+
) => void;
|
|
26
|
+
onStart?: (app: Express) => void;
|
|
27
|
+
middleware?: RequestHandler | RequestHandler[];
|
|
28
|
+
}
|
|
2
29
|
|
|
3
30
|
interface SharedRenderProps {
|
|
4
31
|
routeName: string;
|
package/telemetry/index.js
CHANGED
|
@@ -10,7 +10,7 @@ const { languages } = require('../context');
|
|
|
10
10
|
let projectName = 'unknown';
|
|
11
11
|
let braidVersion = 'unknown';
|
|
12
12
|
try {
|
|
13
|
-
const packageJson = requireFromCwd('package.json');
|
|
13
|
+
const packageJson = requireFromCwd('./package.json');
|
|
14
14
|
|
|
15
15
|
if (packageJson.name) {
|
|
16
16
|
projectName = packageJson.name;
|