@emulsify/core 3.5.0 → 4.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/.cli/init.js +40 -31
- package/.storybook/_drupal.js +129 -8
- package/.storybook/css-components.js +13 -0
- package/.storybook/css-dist.js +5 -0
- package/.storybook/emulsifyTheme.js +9 -6
- package/.storybook/main.js +397 -106
- package/.storybook/manager.js +9 -16
- package/.storybook/preview.js +88 -110
- package/.storybook/utils.js +69 -74
- package/README.md +110 -59
- package/config/.stylelintrc.json +2 -6
- package/config/a11y.config.js +9 -5
- package/config/babel.config.js +6 -11
- package/config/eslint.config.js +31 -3
- package/config/postcss.config.js +5 -0
- package/config/vite/entries.js +227 -0
- package/config/vite/environment.js +39 -0
- package/config/vite/platforms.js +70 -0
- package/config/vite/plugins/copy-src-assets.js +76 -0
- package/config/vite/plugins/copy-twig-files.js +84 -0
- package/config/vite/plugins/css-asset-relativizer.js +40 -0
- package/config/vite/plugins/index.js +105 -0
- package/config/vite/plugins/mirror-components.js +358 -0
- package/config/vite/plugins/require-context.js +311 -0
- package/config/vite/plugins/source-file-index.js +184 -0
- package/config/vite/plugins/svg-sprite.js +117 -0
- package/config/vite/plugins/twig-extension-installers.js +36 -0
- package/config/vite/plugins/twig-module.js +1251 -0
- package/config/vite/plugins/virtual-twig-asset-sources.js +404 -0
- package/config/vite/plugins/virtual-twig-globs.js +136 -0
- package/config/vite/plugins/vituum-patch.js +167 -0
- package/config/vite/plugins/yaml-module.js +133 -0
- package/config/vite/plugins.js +12 -0
- package/config/vite/project-config.js +192 -0
- package/config/vite/project-extensions.js +177 -0
- package/config/vite/project-structure.js +447 -0
- package/config/vite/twig-extensions.js +109 -0
- package/config/vite/utils/fs-safe.js +66 -0
- package/config/vite/utils/paths.js +40 -0
- package/config/vite/utils/react-singleton.js +85 -0
- package/config/vite/utils/unique.js +36 -0
- package/config/vite/vite.config.js +161 -0
- package/package.json +164 -75
- package/scripts/a11y.js +70 -16
- package/scripts/audit-twig-stories.js +378 -0
- package/scripts/audit.js +1602 -0
- package/scripts/check-node-version.js +18 -0
- package/scripts/loadYaml.js +5 -1
- package/src/extensions/index.js +8 -0
- package/src/extensions/react/index.js +12 -0
- package/src/extensions/react/register.js +45 -0
- package/src/extensions/shared/attributes.js +308 -0
- package/src/extensions/shared/html.js +41 -0
- package/src/extensions/shared/lists.js +38 -0
- package/src/extensions/shared/object.js +22 -0
- package/src/extensions/twig/function-map.js +20 -0
- package/src/extensions/twig/functions/add-attributes.js +39 -0
- package/src/extensions/twig/functions/bem.js +166 -0
- package/src/extensions/twig/index.js +13 -0
- package/src/extensions/twig/register.js +95 -0
- package/src/extensions/twig/tag-map.js +16 -0
- package/src/extensions/twig/tags/switch.js +266 -0
- package/src/storybook/index.js +14 -0
- package/src/storybook/main-config.js +132 -0
- package/src/storybook/platform-behaviors.js +60 -0
- package/src/storybook/preview-parameters.js +81 -0
- package/src/storybook/render-twig.js +295 -0
- package/src/storybook/twig/drupal-filters.js +7 -0
- package/src/storybook/twig/include-function.js +109 -0
- package/src/storybook/twig/include.js +28 -0
- package/src/storybook/twig/reference-paths.js +294 -0
- package/src/storybook/twig/resolver.js +318 -0
- package/src/storybook/twig/setup.js +39 -0
- package/src/storybook/twig/source-events.js +5 -0
- package/src/storybook/twig/source-extensions.js +24 -0
- package/src/storybook/twig/source-function.js +239 -0
- package/src/storybook/twig/source.js +39 -0
- package/.all-contributorsrc +0 -45
- package/.editorconfig +0 -5
- package/.github/ISSUE_TEMPLATE/BUG_REPORT_TEMPLATE.md +0 -18
- package/.github/ISSUE_TEMPLATE/FEATURE_REQUEST_TEMPLATE.md +0 -11
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -19
- package/.github/dependabot.yml +0 -6
- package/.github/workflows/addtoprojects.yml +0 -21
- package/.github/workflows/contributors.yml +0 -37
- package/.github/workflows/lint.yml +0 -22
- package/.github/workflows/semantic-release.yml +0 -24
- package/.husky/commit-msg +0 -2
- package/.husky/pre-commit +0 -2
- package/.nvmrc +0 -1
- package/.prettierignore +0 -4
- package/.storybook/polyfills/twig-include.js +0 -40
- package/.storybook/polyfills/twig-resolver.js +0 -70
- package/.storybook/polyfills/twig-source.js +0 -65
- package/.storybook/webpack.config.js +0 -269
- package/CODE_OF_CONDUCT.md +0 -56
- package/commitlint.config.js +0 -5
- package/config/jest.config.js +0 -19
- package/config/webpack/app.js +0 -1
- package/config/webpack/loaders.js +0 -167
- package/config/webpack/optimizers.js +0 -26
- package/config/webpack/plugins.js +0 -283
- package/config/webpack/resolves.js +0 -157
- package/config/webpack/sdc-loader.js +0 -16
- package/config/webpack/webpack.common.js +0 -272
- package/config/webpack/webpack.dev.js +0 -41
- package/config/webpack/webpack.prod.js +0 -6
- package/release.config.cjs +0 -30
- package/scripts/a11y.test.js +0 -172
- package/scripts/loadYaml.test.js +0 -30
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Build Webpack entries and export the configuration.
|
|
3
|
-
* - Discovers JS/SCSS assets (base + component) via glob patterns
|
|
4
|
-
* - Shapes output paths based on platform and SDC (singleDirectoryComponents)
|
|
5
|
-
* - Wires up loaders, plugins, and optimizations
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { posix as path } from 'node:path';
|
|
9
|
-
import { sync as globSync } from 'glob';
|
|
10
|
-
import fs from 'fs-extra';
|
|
11
|
-
|
|
12
|
-
import loaders from './loaders.js';
|
|
13
|
-
import plugins from './plugins.js';
|
|
14
|
-
import resolves from './resolves.js';
|
|
15
|
-
import optimizers from './optimizers.js';
|
|
16
|
-
import emulsifyConfig from '../../../../../project.emulsify.json' with { type: 'json' };
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Resolve the directory of this file (without fileURLToPath).
|
|
20
|
-
* @type {string}
|
|
21
|
-
*/
|
|
22
|
-
let _filename = decodeURIComponent(new URL(import.meta.url).pathname);
|
|
23
|
-
if (process.platform === 'win32' && _filename.startsWith('/')) {
|
|
24
|
-
_filename = _filename.slice(1);
|
|
25
|
-
}
|
|
26
|
-
const _dirname = path.dirname(_filename);
|
|
27
|
-
|
|
28
|
-
/** @type {string} Absolute project root (five levels up from this file). */
|
|
29
|
-
const projectDir = path.resolve(_dirname, '../../../../..');
|
|
30
|
-
|
|
31
|
-
/** @type {boolean} True when a "src/" directory exists (WP layout). */
|
|
32
|
-
const hasSrc = fs.pathExistsSync(path.resolve(projectDir, 'src'));
|
|
33
|
-
|
|
34
|
-
/** @type {string} The canonical source directory ("src" if present, else "components"). */
|
|
35
|
-
const srcDir = hasSrc
|
|
36
|
-
? path.resolve(projectDir, 'src')
|
|
37
|
-
: path.resolve(projectDir, 'components');
|
|
38
|
-
|
|
39
|
-
/** @type {boolean} True when platform is Drupal (affects component output root). */
|
|
40
|
-
const isDrupal = emulsifyConfig?.project?.platform === 'drupal';
|
|
41
|
-
|
|
42
|
-
/** @type {boolean} Respect SDC (single-directory-components) layout if explicitly true. */
|
|
43
|
-
const SDC = Boolean(emulsifyConfig?.project?.singleDirectoryComponents);
|
|
44
|
-
|
|
45
|
-
/** @type {string} Output base for "global" assets. */
|
|
46
|
-
const globalOutBase = hasSrc ? 'dist/global' : 'dist';
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Create a path under the component output root.
|
|
50
|
-
* - In Drupal + src layout, components resolve to "components/…"
|
|
51
|
-
* - Otherwise, they resolve to "dist/components/…"
|
|
52
|
-
* @param {string} subpath - Component-local subpath (no extension).
|
|
53
|
-
* @returns {string} Component output path segment.
|
|
54
|
-
*/
|
|
55
|
-
const componentOutPath = (subpath) =>
|
|
56
|
-
(isDrupal && hasSrc ? 'components' : 'dist/components') + '/' + subpath;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Join segments with POSIX semantics (forward slashes), trimming empties.
|
|
60
|
-
* @param {...string} segs - Path segments.
|
|
61
|
-
* @returns {string} POSIX-joined path.
|
|
62
|
-
*/
|
|
63
|
-
const pj = (...segs) => path.join(...segs.filter(Boolean));
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Compute the “dist subpath” for a non-component asset.
|
|
67
|
-
* Inserts a type folder ("js" or "css") when SDC = false.
|
|
68
|
-
* Drops the original file extension.
|
|
69
|
-
* @param {string} absFile - Absolute file path.
|
|
70
|
-
* @param {'js'|'css'} type - Asset type.
|
|
71
|
-
* @returns {string} Subpath under the global output base (no extension).
|
|
72
|
-
*/
|
|
73
|
-
const distSubpathForBase = (absFile, type) => {
|
|
74
|
-
const rel = path.relative(srcDir, absFile);
|
|
75
|
-
const dir = path.dirname(rel);
|
|
76
|
-
const ext = path.extname(rel);
|
|
77
|
-
const name = path.basename(rel, ext);
|
|
78
|
-
const outTypeDir = type === 'css' ? 'css' : 'js';
|
|
79
|
-
return SDC ? pj(dir, name) : pj(dir, outTypeDir, name);
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Compute the “dist subpath” for a component asset located under "…/components".
|
|
84
|
-
* Inserts a type folder ("js" or "css") when SDC = false.
|
|
85
|
-
* Drops the original file extension.
|
|
86
|
-
* @param {string} absFile - Absolute file path.
|
|
87
|
-
* @param {'js'|'scss'} type - Source type (scss maps to 'css').
|
|
88
|
-
* @returns {string} Component-local subpath (no extension).
|
|
89
|
-
*/
|
|
90
|
-
const distSubpathForComponent = (absFile, type) => {
|
|
91
|
-
const relFromComponents = path.relative(pj(srcDir, 'components'), absFile);
|
|
92
|
-
const dir = path.dirname(relFromComponents);
|
|
93
|
-
const isStyle = type === 'scss';
|
|
94
|
-
const outTypeDir = isStyle ? 'css' : 'js';
|
|
95
|
-
const ext = isStyle ? '.scss' : '.js';
|
|
96
|
-
const name = path.basename(relFromComponents, ext);
|
|
97
|
-
return SDC ? pj(dir, name) : pj(dir, outTypeDir, name);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Sanitize a file path by removing unwanted characters.
|
|
102
|
-
*
|
|
103
|
-
* @param {string} inputPath - The file path to sanitize.
|
|
104
|
-
* @returns {string} The sanitized file path.
|
|
105
|
-
*/
|
|
106
|
-
const sanitizePath = (inputPath) => inputPath.replace(/[^a-zA-Z0-9/_-]/g, '');
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Reject keys that could touch object internals even after sanitization.
|
|
110
|
-
* @param {string} k
|
|
111
|
-
* @returns {boolean}
|
|
112
|
-
*/
|
|
113
|
-
const isDangerousKey = (k) =>
|
|
114
|
-
k.includes('__proto__') || k.includes('prototype') || k === 'constructor';
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Add a file under an entry key; if the key exists, merge to an array.
|
|
118
|
-
* Keeps JS before SCSS for deterministic order.
|
|
119
|
-
*
|
|
120
|
-
* @param {Map<string, string | string[]>} map
|
|
121
|
-
* @param {string} key
|
|
122
|
-
* @param {string} file
|
|
123
|
-
* @returns {void}
|
|
124
|
-
*/
|
|
125
|
-
const addEntry = (map, key, file) => {
|
|
126
|
-
const safeKey = sanitizePath(String(key));
|
|
127
|
-
if (!safeKey || isDangerousKey(safeKey)) return;
|
|
128
|
-
|
|
129
|
-
const current = map.get(safeKey);
|
|
130
|
-
|
|
131
|
-
if (!current) {
|
|
132
|
-
map.set(safeKey, file);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const arr = Array.isArray(current) ? current : [current];
|
|
137
|
-
if (!arr.includes(file)) arr.push(file);
|
|
138
|
-
|
|
139
|
-
// Optional: ensure JS comes before SCSS
|
|
140
|
-
arr.sort((a, b) => {
|
|
141
|
-
const ax = a.endsWith('.js') ? 0 : 1;
|
|
142
|
-
const bx = b.endsWith('.js') ? 0 : 1;
|
|
143
|
-
return ax - bx || a.localeCompare(b);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
map.set(safeKey, arr);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Safe glob wrapper: returns [] if the pattern is falsy.
|
|
151
|
-
* @param {string} pattern - Glob pattern.
|
|
152
|
-
* @returns {string[]} Matching file paths.
|
|
153
|
-
*/
|
|
154
|
-
const glob = (pattern) => (pattern ? globSync(pattern) : []);
|
|
155
|
-
|
|
156
|
-
/* -------------------------------------------------------------------------- */
|
|
157
|
-
/* GLOBS */
|
|
158
|
-
/* -------------------------------------------------------------------------- */
|
|
159
|
-
|
|
160
|
-
const BaseScssPattern = hasSrc
|
|
161
|
-
? pj(srcDir, '!(components|util)/**/!(_*|cl-*|sb-*).scss')
|
|
162
|
-
: '';
|
|
163
|
-
|
|
164
|
-
const ComponentScssPattern = hasSrc
|
|
165
|
-
? pj(srcDir, 'components/**/!(_*|cl-*|sb-*).scss')
|
|
166
|
-
: pj(srcDir, '**/!(_*|cl-*|sb-*).scss');
|
|
167
|
-
|
|
168
|
-
const ComponentLibraryScssPattern = pj(srcDir, '**/*{cl-*,sb-*}.scss');
|
|
169
|
-
|
|
170
|
-
const BaseJsPattern = hasSrc
|
|
171
|
-
? pj(srcDir, '!(components|util)/**/!(*.stories|*.component|*.min|*.test).js')
|
|
172
|
-
: '';
|
|
173
|
-
|
|
174
|
-
const ComponentJsPattern = hasSrc
|
|
175
|
-
? pj(srcDir, 'components/**/!(*.stories|*.component|*.min|*.test).js')
|
|
176
|
-
: pj(srcDir, '**/!(*.stories|*.component|*.min|*.test).js');
|
|
177
|
-
|
|
178
|
-
/* -------------------------------------------------------------------------- */
|
|
179
|
-
/* ENTRY BUILD */
|
|
180
|
-
/* -------------------------------------------------------------------------- */
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Build the complete Webpack entries map.
|
|
184
|
-
* @returns {Record<string,string>} Webpack entries.
|
|
185
|
-
*/
|
|
186
|
-
const buildEntries = () => {
|
|
187
|
-
/** @type {Map<string, string | string[]>} */
|
|
188
|
-
const entries = new Map();
|
|
189
|
-
|
|
190
|
-
/* ----------------------------- Base / Global JS ----------------------------- */
|
|
191
|
-
for (const file of glob(BaseJsPattern)) {
|
|
192
|
-
const sub = distSubpathForBase(file, 'js');
|
|
193
|
-
// If no "src/", legacy layout puts global JS directly under "dist/js".
|
|
194
|
-
const outRoot = hasSrc ? pj(globalOutBase) : pj('dist', 'js');
|
|
195
|
-
addEntry(entries, pj(outRoot, sub), file);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/* --------------------------- Component JS (no dist) -------------------------- */
|
|
199
|
-
for (const file of glob(ComponentJsPattern)) {
|
|
200
|
-
if (file.includes('/dist/')) continue; // guard against accidental recursion
|
|
201
|
-
const sub = distSubpathForComponent(file, 'js');
|
|
202
|
-
addEntry(entries, componentOutPath(sub), file);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/* ------------------------------ Base / Global CSS --------------------------- */
|
|
206
|
-
for (const file of glob(BaseScssPattern)) {
|
|
207
|
-
const sub = distSubpathForBase(file, 'css');
|
|
208
|
-
// If no "src/", legacy layout puts global CSS directly under "dist/css".
|
|
209
|
-
const outRoot = hasSrc ? pj(globalOutBase) : pj('dist', 'css');
|
|
210
|
-
addEntry(entries, pj(outRoot, sub), file);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/* ---------------------------- Component CSS (SCSS) --------------------------- */
|
|
214
|
-
for (const file of glob(ComponentScssPattern)) {
|
|
215
|
-
const sub = distSubpathForComponent(file, 'scss'); // maps to css
|
|
216
|
-
addEntry(entries, componentOutPath(sub), file);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/* -------------------------- Component Library (Storybook) -------------------- */
|
|
220
|
-
for (const file of glob(ComponentLibraryScssPattern)) {
|
|
221
|
-
const rel = path.relative(srcDir, file).replace(/\.scss$/, '');
|
|
222
|
-
addEntry(entries, pj('dist', 'storybook', rel), file);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return Object.fromEntries(entries);
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
/* -------------------------------------------------------------------------- */
|
|
229
|
-
/* WEBPACK CONFIG EXPORT */
|
|
230
|
-
/* -------------------------------------------------------------------------- */
|
|
231
|
-
|
|
232
|
-
export default {
|
|
233
|
-
target: 'web',
|
|
234
|
-
stats: { errorDetails: true },
|
|
235
|
-
externals: {
|
|
236
|
-
drupal: 'Drupal',
|
|
237
|
-
drupalSettings: 'drupalSettings',
|
|
238
|
-
},
|
|
239
|
-
entry: buildEntries(),
|
|
240
|
-
module: {
|
|
241
|
-
rules: [
|
|
242
|
-
loaders.CSSLoader,
|
|
243
|
-
loaders.SVGSpriteLoader,
|
|
244
|
-
loaders.ImageLoader,
|
|
245
|
-
loaders.JSLoader,
|
|
246
|
-
loaders.TwigLoader,
|
|
247
|
-
],
|
|
248
|
-
},
|
|
249
|
-
plugins: [
|
|
250
|
-
plugins.RemoveEmptyJS,
|
|
251
|
-
plugins.MiniCssExtractPlugin,
|
|
252
|
-
plugins.ImageminPlugin,
|
|
253
|
-
plugins.SpritePlugin,
|
|
254
|
-
plugins.ProgressPlugin,
|
|
255
|
-
plugins.CopyTwigPlugin,
|
|
256
|
-
plugins.CopyComponentAssetsPlugin,
|
|
257
|
-
...(plugins.CopyGlobalAssetsPlugin ? [plugins.CopyGlobalAssetsPlugin] : []),
|
|
258
|
-
plugins.CleanWebpackPlugin,
|
|
259
|
-
],
|
|
260
|
-
output: {
|
|
261
|
-
path: projectDir,
|
|
262
|
-
filename: '[name].js',
|
|
263
|
-
},
|
|
264
|
-
resolve: resolves.TwigResolve,
|
|
265
|
-
optimization: optimizers,
|
|
266
|
-
// Quiet deprecation noise from Sass @import warnings
|
|
267
|
-
ignoreWarnings: [
|
|
268
|
-
(warning) =>
|
|
269
|
-
Boolean(warning?.message) &&
|
|
270
|
-
/Sass @import rules are deprecated/.test(warning.message),
|
|
271
|
-
],
|
|
272
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import { resolve, dirname } from 'path';
|
|
3
|
-
import { merge } from 'webpack-merge';
|
|
4
|
-
import common from './webpack.common.js';
|
|
5
|
-
|
|
6
|
-
// JSON import syntax may vary; adjust if you need `assert { type: 'json' }` instead
|
|
7
|
-
import emulsifyConfig from '../../../../../project.emulsify.json' with { type: 'json' };
|
|
8
|
-
|
|
9
|
-
// Create __filename from import.meta.url without fileURLToPath
|
|
10
|
-
let _filename = decodeURIComponent(new URL(import.meta.url).pathname);
|
|
11
|
-
|
|
12
|
-
// On Windows, remove the leading slash (e.g. "/C:/path" -> "C:/path")
|
|
13
|
-
if (process.platform === 'win32' && _filename.startsWith('/')) {
|
|
14
|
-
_filename = _filename.slice(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const _dirname = dirname(_filename);
|
|
18
|
-
|
|
19
|
-
// Get directories for file contexts.
|
|
20
|
-
const projectDir = resolve(_dirname, '../../../../..');
|
|
21
|
-
|
|
22
|
-
const srcPath = resolve(projectDir, 'src');
|
|
23
|
-
const srcExists = fs.pathExistsSync(srcPath);
|
|
24
|
-
const isDrupal = emulsifyConfig.project.platform === 'drupal';
|
|
25
|
-
|
|
26
|
-
// Always ignore dist
|
|
27
|
-
const ignored = ['**/dist/**'];
|
|
28
|
-
|
|
29
|
-
// If it’s Drupal and there is no src/, also ignore components
|
|
30
|
-
if (isDrupal && !srcExists) {
|
|
31
|
-
ignored.push('**/components/**');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export default merge(common, {
|
|
35
|
-
mode: 'development',
|
|
36
|
-
devtool: 'source-map',
|
|
37
|
-
watch: true,
|
|
38
|
-
watchOptions: {
|
|
39
|
-
ignored,
|
|
40
|
-
},
|
|
41
|
-
});
|
package/release.config.cjs
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// release.config.cjs
|
|
2
|
-
module.exports = {
|
|
3
|
-
branches: ['main'],
|
|
4
|
-
repositoryUrl: 'git@github.com:emulsify-ds/emulsify-core.git',
|
|
5
|
-
plugins: [
|
|
6
|
-
[
|
|
7
|
-
'@semantic-release/commit-analyzer',
|
|
8
|
-
{
|
|
9
|
-
preset: 'angular',
|
|
10
|
-
parserOpts: {
|
|
11
|
-
noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'BREAKING']
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
],
|
|
15
|
-
[
|
|
16
|
-
'@semantic-release/release-notes-generator',
|
|
17
|
-
{
|
|
18
|
-
preset: 'angular',
|
|
19
|
-
parserOpts: {
|
|
20
|
-
noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'BREAKING']
|
|
21
|
-
},
|
|
22
|
-
writerOpts: {
|
|
23
|
-
commitsSort: ['subject', 'scope']
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
],
|
|
27
|
-
['@semantic-release/npm', { npmPublish: true }],
|
|
28
|
-
'@semantic-release/github'
|
|
29
|
-
]
|
|
30
|
-
}
|
package/scripts/a11y.test.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import 'regenerator-runtime/runtime';
|
|
2
|
-
|
|
3
|
-
const mockExit = jest
|
|
4
|
-
.spyOn(global.process, 'exit')
|
|
5
|
-
.mockImplementation(() => {});
|
|
6
|
-
jest.mock('pa11y', () => jest.fn());
|
|
7
|
-
jest.spyOn(global.console, 'log').mockImplementation(() => {});
|
|
8
|
-
const pa11y = require('pa11y');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const {
|
|
11
|
-
severityToColor,
|
|
12
|
-
issueIsValid,
|
|
13
|
-
logIssue,
|
|
14
|
-
logReport,
|
|
15
|
-
lintComponent,
|
|
16
|
-
lintReportAndExit,
|
|
17
|
-
} = require('./a11y');
|
|
18
|
-
const {
|
|
19
|
-
ignore,
|
|
20
|
-
storybookBuildDir,
|
|
21
|
-
pa11y: pa11yConfig,
|
|
22
|
-
} = require('../config/a11y.config');
|
|
23
|
-
|
|
24
|
-
const STORYBOOK_BUILD_DIR = path.resolve(__dirname, '../', storybookBuildDir);
|
|
25
|
-
const STORYBOOK_IFRAME = path.join(STORYBOOK_BUILD_DIR, 'iframe.html');
|
|
26
|
-
|
|
27
|
-
pa11y.mockResolvedValue('very official report');
|
|
28
|
-
|
|
29
|
-
describe('a11y', () => {
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
global.console.log.mockClear();
|
|
32
|
-
global.process.exit.mockClear();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('maps axe issue severity to a label', () => {
|
|
36
|
-
// (Name no longer mentions "chalk")
|
|
37
|
-
expect.assertions(3);
|
|
38
|
-
expect(severityToColor('error')).toBe('red');
|
|
39
|
-
expect(severityToColor('warning')).toBe('yellow');
|
|
40
|
-
expect(severityToColor('notice')).toBe('blue');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('identifies invalid issues based on the code or the description', () => {
|
|
44
|
-
expect.assertions(3);
|
|
45
|
-
expect(
|
|
46
|
-
issueIsValid({
|
|
47
|
-
code: ignore.codes[0],
|
|
48
|
-
runnerExtras: {},
|
|
49
|
-
}),
|
|
50
|
-
).toBe(false);
|
|
51
|
-
expect(
|
|
52
|
-
issueIsValid({
|
|
53
|
-
runnerExtras: {
|
|
54
|
-
description: ignore.descriptions[0],
|
|
55
|
-
},
|
|
56
|
-
}),
|
|
57
|
-
).toBe(false);
|
|
58
|
-
expect(issueIsValid({ code: 'chicken', runnerExtras: {} })).toBe(true);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('logs a single issue without color codes', () => {
|
|
62
|
-
expect.assertions(1);
|
|
63
|
-
logIssue({
|
|
64
|
-
type: 'error',
|
|
65
|
-
message: 'this chicken is not fried enough.',
|
|
66
|
-
context: 'https://example.com',
|
|
67
|
-
selector: 'kfc > popeyes > .chicken',
|
|
68
|
-
});
|
|
69
|
-
expect(global.console.log.mock.calls[0][0]).toMatchInlineSnapshot(`
|
|
70
|
-
"
|
|
71
|
-
severity: error
|
|
72
|
-
message: this chicken is not fried enough.
|
|
73
|
-
context: https://example.com
|
|
74
|
-
selector: kfc > popeyes > .chicken
|
|
75
|
-
"
|
|
76
|
-
`);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('logs a whole report without color codes', () => {
|
|
80
|
-
const report = {
|
|
81
|
-
issues: [
|
|
82
|
-
{
|
|
83
|
-
type: 'error',
|
|
84
|
-
message: 'this pizza is too soggy',
|
|
85
|
-
context: 'https://example.com',
|
|
86
|
-
selector: 'pizza > .hut',
|
|
87
|
-
runnerExtras: {},
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
type: 'error',
|
|
91
|
-
message: 'this pasta is undercooked',
|
|
92
|
-
context: 'https://example.com',
|
|
93
|
-
selector: 'olive > .garden',
|
|
94
|
-
runnerExtras: {},
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
pageUrl: 'https://example/component.html',
|
|
98
|
-
};
|
|
99
|
-
expect(logReport(report)).toBe(true);
|
|
100
|
-
expect(global.console.log.mock.calls).toMatchInlineSnapshot(`
|
|
101
|
-
Array [
|
|
102
|
-
Array [
|
|
103
|
-
"Issues found in component: https://example/component.html",
|
|
104
|
-
],
|
|
105
|
-
Array [
|
|
106
|
-
"
|
|
107
|
-
severity: error
|
|
108
|
-
message: this pizza is too soggy
|
|
109
|
-
context: https://example.com
|
|
110
|
-
selector: pizza > .hut
|
|
111
|
-
",
|
|
112
|
-
],
|
|
113
|
-
Array [
|
|
114
|
-
"
|
|
115
|
-
severity: error
|
|
116
|
-
message: this pasta is undercooked
|
|
117
|
-
context: https://example.com
|
|
118
|
-
selector: olive > .garden
|
|
119
|
-
",
|
|
120
|
-
],
|
|
121
|
-
]
|
|
122
|
-
`);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('logs that a component has no issues when a report is empty', () => {
|
|
126
|
-
expect(logReport({ issues: [], pageUrl: 'papa-johns' })).toBe(false);
|
|
127
|
-
expect(global.console.log.mock.calls[0][0]).toMatchInlineSnapshot(
|
|
128
|
-
`"No issues found in component: papa-johns"`,
|
|
129
|
-
);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('calls pa11y with the full path to a component', async () => {
|
|
133
|
-
expect.assertions(3);
|
|
134
|
-
await expect(lintComponent('chicken-strips')).resolves.toBe(
|
|
135
|
-
'very official report',
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
// First arg: URL
|
|
139
|
-
expect(pa11y.mock.calls[0][0]).toBe(
|
|
140
|
-
`${STORYBOOK_IFRAME}?id=chicken-strips`,
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
// Second arg: options merged with defaults in a11y.js
|
|
144
|
-
expect(pa11y.mock.calls[0][1]).toEqual(
|
|
145
|
-
expect.objectContaining({
|
|
146
|
-
includeNotices: true,
|
|
147
|
-
includeWarnings: true,
|
|
148
|
-
runners: ['axe'],
|
|
149
|
-
...pa11yConfig,
|
|
150
|
-
}),
|
|
151
|
-
);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('runs linter, reports on issues, and exits with code "1" if valid issues are found', async () => {
|
|
155
|
-
expect.assertions(1);
|
|
156
|
-
pa11y.mockResolvedValueOnce({
|
|
157
|
-
issues: [
|
|
158
|
-
{
|
|
159
|
-
type: 'error',
|
|
160
|
-
message: 'these 7 layer supreme burritos do not taste that good',
|
|
161
|
-
context: 'https://example.com',
|
|
162
|
-
selector: 'taco > bell > .burrito',
|
|
163
|
-
runnerExtras: {},
|
|
164
|
-
},
|
|
165
|
-
],
|
|
166
|
-
pageUrl: '/path/to/taco-bell',
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
await lintReportAndExit(['taco-bell']);
|
|
170
|
-
expect(global.process.exit).toHaveBeenCalledWith(1);
|
|
171
|
-
});
|
|
172
|
-
});
|
package/scripts/loadYaml.test.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import yaml from 'yaml';
|
|
3
|
-
import loadYaml from './loadYaml';
|
|
4
|
-
|
|
5
|
-
jest
|
|
6
|
-
.spyOn(fs, 'readFileSync')
|
|
7
|
-
.mockImplementation(() => 'yaml spaghetti and meatballs');
|
|
8
|
-
|
|
9
|
-
jest.spyOn(yaml, 'parse').mockImplementation(() => ({
|
|
10
|
-
the: 'yaml spaghetti and meatballs',
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
describe('loadYaml', () => {
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
fs.readFileSync.mockClear();
|
|
16
|
-
yaml.parse.mockClear();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('can load a yaml file, parse it, and return it', () => {
|
|
20
|
-
expect.assertions(3);
|
|
21
|
-
expect(loadYaml('./big-phat-burger.yml')).toEqual({
|
|
22
|
-
the: 'yaml spaghetti and meatballs',
|
|
23
|
-
});
|
|
24
|
-
expect(fs.readFileSync).toHaveBeenCalledWith(
|
|
25
|
-
`${__dirname}/big-phat-burger.yml`,
|
|
26
|
-
'utf8',
|
|
27
|
-
);
|
|
28
|
-
expect(yaml.parse).toHaveBeenCalledWith('yaml spaghetti and meatballs');
|
|
29
|
-
});
|
|
30
|
-
});
|