@emulsify/core 2.7.1 → 3.0.2
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/.github/workflows/semantic-release.yml +3 -3
- package/.nvmrc +1 -1
- package/.storybook/_drupal.js +22 -0
- package/.storybook/emulsifyTheme.js +1 -2
- package/.storybook/main.js +116 -31
- package/.storybook/manager.js +40 -9
- package/.storybook/preview.js +71 -19
- package/.storybook/utils.js +29 -19
- package/.storybook/webpack.config.js +95 -26
- package/commitlint.config.js +4 -2
- package/config/babel.config.js +1 -2
- package/config/eslint.config.js +74 -0
- package/config/postcss.config.js +2 -2
- package/config/webpack/loaders.js +25 -16
- package/config/webpack/optimizers.js +2 -2
- package/config/webpack/plugins.js +37 -33
- package/config/webpack/resolves.js +42 -35
- package/config/webpack/sdc-loader.js +10 -2
- package/config/webpack/webpack.common.js +103 -95
- package/config/webpack/webpack.dev.js +3 -3
- package/config/webpack/webpack.prod.js +3 -3
- package/package.json +46 -37
- package/release.config.cjs +30 -0
- package/.eslintignore +0 -2
- package/config/eslintrc.config.json +0 -76
- package/config/webpack/svgSprite.js +0 -5
- package/release.config.js +0 -11
|
@@ -1,30 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { dirname, resolve } from 'path';
|
|
2
|
+
import globImporter from 'node-sass-glob-importer';
|
|
3
|
+
import _StyleLintPlugin from 'stylelint-webpack-plugin';
|
|
4
|
+
import ESLintPlugin from 'eslint-webpack-plugin';
|
|
5
|
+
import resolves from '../config/webpack/resolves.js';
|
|
6
|
+
import emulsifyConfig from '../../../../project.emulsify.json' with { type: 'json' };
|
|
3
7
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const resolves = require('../config/webpack/resolves');
|
|
8
|
+
// Create __filename from import.meta.url without fileURLToPath
|
|
9
|
+
let _filename = decodeURIComponent(new URL(import.meta.url).pathname);
|
|
7
10
|
|
|
8
|
-
//
|
|
9
|
-
|
|
11
|
+
// On Windows, remove the leading slash (e.g. "/C:/path" -> "C:/path")
|
|
12
|
+
if (process.platform === 'win32' && _filename.startsWith('/')) {
|
|
13
|
+
_filename = _filename.slice(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Directory name of the current file.
|
|
18
|
+
* @type {string}
|
|
19
|
+
*/
|
|
20
|
+
const _dirname = dirname(_filename);
|
|
10
21
|
|
|
11
22
|
/**
|
|
12
|
-
*
|
|
23
|
+
* Absolute path to the project root directory.
|
|
24
|
+
* @type {string}
|
|
25
|
+
*/
|
|
26
|
+
const projectDir = resolve(_dirname, '../../../../..');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Webpack plugin to resolve custom namespace imports.
|
|
30
|
+
* Transforms `<prefix>:<component>` into `<prefix>/<component>` paths.
|
|
13
31
|
*/
|
|
14
32
|
class ProjectNameResolverPlugin {
|
|
33
|
+
/**
|
|
34
|
+
* @param {object} options - Plugin options.
|
|
35
|
+
* @param {string} options.projectName - Prefix for the project namespace.
|
|
36
|
+
*/
|
|
15
37
|
constructor(options = {}) {
|
|
16
38
|
this.prefix = options.projectName;
|
|
17
39
|
}
|
|
18
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Apply the webpack resolver hook.
|
|
43
|
+
* @param {object} resolver - The webpack resolver instance.
|
|
44
|
+
*/
|
|
19
45
|
apply(resolver) {
|
|
20
46
|
const target = resolver.ensureHook('resolve');
|
|
21
|
-
resolver
|
|
22
|
-
|
|
23
|
-
|
|
47
|
+
resolver.getHook('before-resolve').tapAsync(
|
|
48
|
+
'ProjectNameResolverPlugin',
|
|
49
|
+
/**
|
|
50
|
+
* @param {object} request - The resolve request object.
|
|
51
|
+
* @param {object} resolveContext - Context for resolving.
|
|
52
|
+
* @param {Function} callback - Callback to continue resolution.
|
|
53
|
+
*/
|
|
54
|
+
(request, resolveContext, callback) => {
|
|
24
55
|
const requestPath = request.request;
|
|
25
56
|
|
|
26
|
-
if (
|
|
27
|
-
|
|
57
|
+
if (
|
|
58
|
+
requestPath &&
|
|
59
|
+
requestPath.startsWith(`${this.prefix}:`)
|
|
60
|
+
) {
|
|
61
|
+
const newRequestPath = requestPath.replace(
|
|
62
|
+
`${this.prefix}:`,
|
|
63
|
+
`${this.prefix}/`
|
|
64
|
+
);
|
|
28
65
|
const newRequest = {
|
|
29
66
|
...request,
|
|
30
67
|
request: newRequestPath,
|
|
@@ -40,31 +77,53 @@ class ProjectNameResolverPlugin {
|
|
|
40
77
|
} else {
|
|
41
78
|
callback();
|
|
42
79
|
}
|
|
43
|
-
}
|
|
80
|
+
}
|
|
81
|
+
);
|
|
44
82
|
}
|
|
45
83
|
}
|
|
46
84
|
|
|
47
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Export a function to customize the Webpack config for Storybook.
|
|
87
|
+
* @param {object} param0 - The Storybook configuration object.
|
|
88
|
+
* @param {object} param0.config - The existing webpack config to modify.
|
|
89
|
+
* @returns {object} The updated webpack config.
|
|
90
|
+
*/
|
|
91
|
+
export default async function ({ config }) {
|
|
48
92
|
// Alias
|
|
49
93
|
Object.assign(config.resolve.alias, resolves.TwigResolve.alias);
|
|
50
94
|
|
|
51
|
-
// Twig
|
|
95
|
+
// Twig loader
|
|
52
96
|
config.module.rules.push({
|
|
97
|
+
/**
|
|
98
|
+
* @type {RegExp}
|
|
99
|
+
*/
|
|
53
100
|
test: /\.twig$/,
|
|
54
101
|
use: [
|
|
55
102
|
{
|
|
56
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Custom loader for svg/spritemap integration.
|
|
105
|
+
* @type {string}
|
|
106
|
+
*/
|
|
107
|
+
loader: resolve(_dirname, '../config/webpack/sdc-loader.js'),
|
|
57
108
|
options: {
|
|
109
|
+
/**
|
|
110
|
+
* Name of the Emulsify project for resolving.
|
|
111
|
+
* @type {string}
|
|
112
|
+
*/
|
|
58
113
|
projectName: emulsifyConfig.project.name,
|
|
59
114
|
},
|
|
60
115
|
},
|
|
61
116
|
{
|
|
117
|
+
/**
|
|
118
|
+
* Standard Twig JS loader.
|
|
119
|
+
* @type {string}
|
|
120
|
+
*/
|
|
62
121
|
loader: 'twigjs-loader',
|
|
63
122
|
},
|
|
64
123
|
],
|
|
65
124
|
});
|
|
66
125
|
|
|
67
|
-
// SCSS
|
|
126
|
+
// SCSS Loader configuration
|
|
68
127
|
config.module.rules.push({
|
|
69
128
|
test: /\.s[ac]ss$/i,
|
|
70
129
|
use: [
|
|
@@ -72,6 +131,10 @@ module.exports = async ({ config }) => {
|
|
|
72
131
|
{
|
|
73
132
|
loader: 'css-loader',
|
|
74
133
|
options: {
|
|
134
|
+
/**
|
|
135
|
+
* Enable source maps for CSS.
|
|
136
|
+
* @type {boolean}
|
|
137
|
+
*/
|
|
75
138
|
sourceMap: true,
|
|
76
139
|
},
|
|
77
140
|
},
|
|
@@ -87,38 +150,44 @@ module.exports = async ({ config }) => {
|
|
|
87
150
|
],
|
|
88
151
|
});
|
|
89
152
|
|
|
90
|
-
// YAML
|
|
153
|
+
// YAML loader
|
|
91
154
|
config.module.rules.push({
|
|
155
|
+
/**
|
|
156
|
+
* @type {RegExp}
|
|
157
|
+
*/
|
|
92
158
|
test: /\.ya?ml$/,
|
|
93
159
|
loader: 'js-yaml-loader',
|
|
94
160
|
});
|
|
95
161
|
|
|
96
|
-
//
|
|
162
|
+
// StyleLint and ESLint plugins
|
|
97
163
|
config.plugins.push(
|
|
98
164
|
new _StyleLintPlugin({
|
|
99
|
-
configFile:
|
|
100
|
-
context:
|
|
165
|
+
configFile: resolve(projectDir, '../', '.stylelintrc.json'),
|
|
166
|
+
context: resolve(projectDir, '../', 'src'),
|
|
101
167
|
files: '**/*.scss',
|
|
102
168
|
failOnError: false,
|
|
103
169
|
quiet: false,
|
|
104
170
|
}),
|
|
105
171
|
new ESLintPlugin({
|
|
106
|
-
context:
|
|
172
|
+
context: resolve(projectDir, '../', 'src'),
|
|
107
173
|
extensions: ['js'],
|
|
108
174
|
}),
|
|
109
175
|
);
|
|
110
176
|
|
|
111
|
-
//
|
|
177
|
+
// Custom resolver plugin for namespaced imports
|
|
112
178
|
config.resolve.plugins = [
|
|
113
179
|
new ProjectNameResolverPlugin({
|
|
114
180
|
projectName: emulsifyConfig.project.name,
|
|
115
181
|
}),
|
|
116
182
|
];
|
|
117
183
|
|
|
118
|
-
//
|
|
184
|
+
// Fallback for optional modules
|
|
119
185
|
config.resolve.fallback = {
|
|
186
|
+
/**
|
|
187
|
+
* Prevent resolution of components directory if missing.
|
|
188
|
+
*/
|
|
120
189
|
'../../../../components': false,
|
|
121
190
|
};
|
|
122
191
|
|
|
123
192
|
return config;
|
|
124
|
-
}
|
|
193
|
+
}
|
package/commitlint.config.js
CHANGED
package/config/babel.config.js
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Import ESLint Flat Config and required plugins
|
|
2
|
+
import js from '@eslint/js';
|
|
3
|
+
import babelParser from '@babel/eslint-parser';
|
|
4
|
+
import importPlugin from 'eslint-plugin-import';
|
|
5
|
+
import pluginSecurity from 'eslint-plugin-security';
|
|
6
|
+
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
7
|
+
|
|
8
|
+
export default [
|
|
9
|
+
// Base ESLint recommended rules
|
|
10
|
+
js.configs.recommended,
|
|
11
|
+
|
|
12
|
+
// Plugin configurations
|
|
13
|
+
importPlugin.flatConfigs.recommended,
|
|
14
|
+
pluginSecurity.configs.recommended,
|
|
15
|
+
eslintPluginPrettierRecommended,
|
|
16
|
+
|
|
17
|
+
{
|
|
18
|
+
name: 'emulsify-core-config',
|
|
19
|
+
languageOptions: {
|
|
20
|
+
parser: babelParser,
|
|
21
|
+
parserOptions: {
|
|
22
|
+
requireConfigFile: false,
|
|
23
|
+
babelOptions: {
|
|
24
|
+
babelrc: false,
|
|
25
|
+
configFile: false,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
sourceType: 'module',
|
|
29
|
+
ecmaVersion: 'latest',
|
|
30
|
+
globals: {
|
|
31
|
+
expect: true,
|
|
32
|
+
it: true,
|
|
33
|
+
describe: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
files: ['**/*.{js,mjs,cjs}'],
|
|
38
|
+
|
|
39
|
+
ignores: ['**/*.min.js', '**/node_modules/**/*'],
|
|
40
|
+
|
|
41
|
+
rules: {
|
|
42
|
+
strict: 0,
|
|
43
|
+
'consistent-return': 'off',
|
|
44
|
+
'no-underscore-dangle': 'off',
|
|
45
|
+
'max-nested-callbacks': ['warn', 3],
|
|
46
|
+
'import/extensions': 'off',
|
|
47
|
+
'import/no-unresolved': 'off',
|
|
48
|
+
'import/no-extraneous-dependencies': 'warn',
|
|
49
|
+
'import/no-mutable-exports': 'warn',
|
|
50
|
+
'no-plusplus': ['warn', { allowForLoopAfterthoughts: true }],
|
|
51
|
+
'no-param-reassign': 'off',
|
|
52
|
+
'no-prototype-builtins': 'off',
|
|
53
|
+
'prettier/prettier': ['error', { singleQuote: true }],
|
|
54
|
+
'no-unused-vars': 'warn',
|
|
55
|
+
'no-undef': 'off',
|
|
56
|
+
'operator-linebreak': [
|
|
57
|
+
'error',
|
|
58
|
+
'after',
|
|
59
|
+
{ overrides: { '?': 'ignore', ':': 'ignore' } },
|
|
60
|
+
],
|
|
61
|
+
quotes: ['error', 'single'],
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
settings: {
|
|
65
|
+
'import/ignore': ['\\.(scss|less|css)$'],
|
|
66
|
+
'import/resolver': {
|
|
67
|
+
node: {
|
|
68
|
+
extensions: ['.js', '.jsx'],
|
|
69
|
+
moduleDirectory: ['src', 'node_modules'],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
];
|
package/config/postcss.config.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configures Webpack loaders.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
6
|
+
import globImporter from 'node-sass-glob-importer';
|
|
7
|
+
import fs from 'fs-extra';
|
|
4
8
|
|
|
5
9
|
let babelConfig;
|
|
6
10
|
let postcssConfig;
|
|
7
11
|
|
|
8
12
|
// Check if custom babel config is available.
|
|
9
|
-
if (fs.existsSync('./config/babel.config.
|
|
10
|
-
babelConfig = './config/babel.config.
|
|
13
|
+
if (fs.existsSync('./config/emulsify-core/webpack/babel.config.cjs')) {
|
|
14
|
+
babelConfig = './config/emulsify-core/webpack/babel.config.cjs';
|
|
11
15
|
} else {
|
|
12
16
|
babelConfig = './node_modules/@emulsify/core/config/babel.config.js';
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
// Check if custom postcss config is available.
|
|
16
|
-
if (fs.existsSync('./config/postcss.config.
|
|
17
|
-
postcssConfig = './config/postcss.config.
|
|
20
|
+
if (fs.existsSync('./config/emulsify-core/webpack/postcss.config.cjs')) {
|
|
21
|
+
postcssConfig = './config/emulsify-core/webpack/postcss.config.cjs';
|
|
18
22
|
} else {
|
|
19
23
|
postcssConfig = './node_modules/@emulsify/core/config/postcss.config.js';
|
|
20
24
|
}
|
|
@@ -71,14 +75,19 @@ const CSSLoader = {
|
|
|
71
75
|
};
|
|
72
76
|
|
|
73
77
|
const SVGSpriteLoader = {
|
|
74
|
-
test: /icons\/.*\.svg$/,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
test: /icons\/.*\.svg$/,
|
|
79
|
+
use: [
|
|
80
|
+
{
|
|
81
|
+
loader: 'svg-sprite-loader',
|
|
82
|
+
options: {
|
|
83
|
+
extract: true,
|
|
84
|
+
esModule: true,
|
|
85
|
+
runtimeCompat: true,
|
|
86
|
+
outputPath: 'dist/',
|
|
87
|
+
spriteFilename: './icons.svg',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
82
91
|
};
|
|
83
92
|
|
|
84
93
|
const TwigLoader = {
|
|
@@ -88,7 +97,7 @@ const TwigLoader = {
|
|
|
88
97
|
},
|
|
89
98
|
};
|
|
90
99
|
|
|
91
|
-
|
|
100
|
+
export default {
|
|
92
101
|
JSLoader,
|
|
93
102
|
CSSLoader,
|
|
94
103
|
SVGSpriteLoader,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import ImageMinimizerPlugin from 'image-minimizer-webpack-plugin';
|
|
2
2
|
|
|
3
3
|
const ImageMinimizer = new ImageMinimizerPlugin({
|
|
4
4
|
minimizer: {
|
|
@@ -13,6 +13,6 @@ const ImageMinimizer = new ImageMinimizerPlugin({
|
|
|
13
13
|
},
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
export default {
|
|
17
17
|
minimizer: [ImageMinimizer],
|
|
18
18
|
};
|
|
@@ -1,45 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configures Webpack plugins.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { resolve, dirname } from 'path';
|
|
6
|
+
import webpack from 'webpack';
|
|
7
|
+
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
|
|
8
|
+
import _MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
9
|
+
import _SpriteLoaderPlugin from 'svg-sprite-loader/plugin.js';
|
|
10
|
+
import CopyPlugin from 'copy-webpack-plugin';
|
|
11
|
+
import { sync as globSync } from 'glob';
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
import emulsifyConfig from '../../../../../project.emulsify.json' with { type: 'json' };
|
|
14
|
+
|
|
15
|
+
// Create __filename from import.meta.url without fileURLToPath
|
|
16
|
+
let _filename = decodeURIComponent(new URL(import.meta.url).pathname);
|
|
10
17
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
// On Windows, remove the leading slash (e.g. "/C:/path" -> "C:/path")
|
|
19
|
+
if (process.platform === 'win32' && _filename.startsWith('/')) {
|
|
20
|
+
_filename = _filename.slice(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const _dirname = dirname(_filename);
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
const
|
|
25
|
+
const projectDir = resolve(_dirname, '../../../../..');
|
|
26
|
+
const srcDir = resolve(projectDir, 'src');
|
|
17
27
|
|
|
18
|
-
// Compress images plugin.
|
|
19
28
|
const MiniCssExtractPlugin = new _MiniCssExtractPlugin({
|
|
20
29
|
filename: '[name].css',
|
|
21
30
|
chunkFilename: '[id].css',
|
|
22
31
|
});
|
|
23
32
|
|
|
24
|
-
// Create SVG sprite.
|
|
25
33
|
const SpriteLoaderPlugin = new _SpriteLoaderPlugin({
|
|
26
34
|
plainSprite: true,
|
|
27
35
|
});
|
|
28
36
|
|
|
29
|
-
// Enable Webpack progress plugin.
|
|
30
37
|
const ProgressPlugin = new webpack.ProgressPlugin();
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
const componentFilesPattern = path.resolve(srcDir, '**/*.{twig,component.yml}');
|
|
39
|
+
const componentFilesPattern = resolve(srcDir, '**/*.{twig,component.yml}');
|
|
34
40
|
|
|
35
41
|
/**
|
|
36
|
-
* Prepare list of
|
|
37
|
-
*
|
|
38
|
-
* @param {string} filesMatcher - Glob pattern.
|
|
42
|
+
* Prepare a list of patterns for copying Twig and component files.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} filesMatcher - Glob pattern for matching files.
|
|
45
|
+
* @returns {Array<Object>} Array of objects with `from` and `to` properties.
|
|
39
46
|
*/
|
|
40
47
|
function getPatterns(filesMatcher) {
|
|
41
48
|
const patterns = [];
|
|
42
|
-
|
|
49
|
+
globSync(filesMatcher).forEach((file) => {
|
|
43
50
|
const projectPath = file.split('/src/')[0];
|
|
44
51
|
const srcStructure = file.split(`${srcDir}/`)[1];
|
|
45
52
|
const parentDir = srcStructure.split('/')[0];
|
|
@@ -57,32 +64,29 @@ function getPatterns(filesMatcher) {
|
|
|
57
64
|
to: newfilePath,
|
|
58
65
|
});
|
|
59
66
|
});
|
|
60
|
-
|
|
61
67
|
return patterns;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
? new CopyPlugin({
|
|
67
|
-
patterns: getPatterns(componentFilesPattern),
|
|
68
|
-
})
|
|
70
|
+
const CopyTwigPlugin = fs.pathExistsSync(resolve(projectDir, 'src'))
|
|
71
|
+
? new CopyPlugin({ patterns: getPatterns(componentFilesPattern) })
|
|
69
72
|
: '';
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
module.exports = {
|
|
74
|
+
const pluginConfig = {
|
|
73
75
|
ProgressPlugin,
|
|
74
76
|
MiniCssExtractPlugin,
|
|
75
77
|
SpriteLoaderPlugin,
|
|
76
78
|
CopyTwigPlugin,
|
|
77
79
|
CleanWebpackPlugin: new CleanWebpackPlugin({
|
|
78
|
-
protectWebpackAssets: false,
|
|
80
|
+
protectWebpackAssets: false,
|
|
79
81
|
cleanOnceBeforeBuildPatterns: ['!*.{png,jpg,gif,svg}'],
|
|
80
82
|
cleanAfterEveryBuildPatterns: [
|
|
81
83
|
'remove/**',
|
|
82
84
|
'!js',
|
|
83
|
-
'css/**/*.js',
|
|
85
|
+
'css/**/*.js',
|
|
84
86
|
'css/**/*.js.map',
|
|
85
87
|
'!*.{png,jpg,gif,svg}',
|
|
86
88
|
],
|
|
87
89
|
}),
|
|
88
90
|
};
|
|
91
|
+
|
|
92
|
+
export default pluginConfig;
|
|
@@ -1,37 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configures Twig alias resolution for the project.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { basename, dirname, resolve } from 'path';
|
|
6
|
+
import { sync as globSync } from 'glob';
|
|
7
|
+
import fs from 'fs-extra';
|
|
8
|
+
import emulsifyConfig from '../../../../../project.emulsify.json' with { type: 'json' };
|
|
4
9
|
|
|
5
|
-
//
|
|
6
|
-
|
|
10
|
+
// Create __filename from import.meta.url without fileURLToPath
|
|
11
|
+
let _filename = decodeURIComponent(new URL(import.meta.url).pathname);
|
|
12
|
+
|
|
13
|
+
// On Windows, remove the leading slash (e.g. "/C:/path" -> "C:/path")
|
|
14
|
+
if (process.platform === 'win32' && _filename.startsWith('/')) {
|
|
15
|
+
_filename = _filename.slice(1);
|
|
16
|
+
}
|
|
7
17
|
|
|
8
|
-
|
|
9
|
-
|
|
18
|
+
const _dirname = dirname(_filename);
|
|
19
|
+
|
|
20
|
+
const projectDir = resolve(_dirname, '../../../../..');
|
|
10
21
|
const projectName = emulsifyConfig.project.name;
|
|
11
|
-
const srcDir = fs.
|
|
12
|
-
?
|
|
13
|
-
:
|
|
22
|
+
const srcDir = fs.pathExistsSync(resolve(projectDir, 'src'))
|
|
23
|
+
? resolve(projectDir, 'src')
|
|
24
|
+
: resolve(projectDir, 'components');
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
const aliasPattern = path.resolve(srcDir, '**/!(_*).twig');
|
|
26
|
+
const aliasPattern = resolve(srcDir, '**/!(_*).twig');
|
|
17
27
|
|
|
18
28
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @param {string} source -
|
|
29
|
+
* Get all top-level directory names from a source directory.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} source - The source directory path.
|
|
32
|
+
* @returns {string[]} Array of directory names.
|
|
22
33
|
*/
|
|
23
34
|
function getDirectories(source) {
|
|
35
|
+
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
24
36
|
const dirs = fs
|
|
25
|
-
.readdirSync(source, { withFileTypes: true })
|
|
26
|
-
.filter((dirent) => dirent.isDirectory())
|
|
37
|
+
.readdirSync(source, { withFileTypes: true })
|
|
38
|
+
.filter((dirent) => dirent.isDirectory())
|
|
27
39
|
.map((dirent) => dirent.name);
|
|
40
|
+
/* eslint-enable security/detect-non-literal-fs-filename */
|
|
28
41
|
return dirs;
|
|
29
42
|
}
|
|
30
43
|
|
|
31
44
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* @param {string} dir -
|
|
45
|
+
* Remove numbering from a directory name if present.
|
|
46
|
+
*
|
|
47
|
+
* @param {string} dir - The original directory name.
|
|
48
|
+
* @returns {string} The cleaned directory name.
|
|
35
49
|
*/
|
|
36
50
|
function cleanDirectoryName(dir) {
|
|
37
51
|
if (/^\d{2}/.test(dir)) {
|
|
@@ -41,40 +55,33 @@ function cleanDirectoryName(dir) {
|
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* @param {string} aliasMatcher -
|
|
58
|
+
* Generate a set of Twig aliases from a glob pattern.
|
|
59
|
+
*
|
|
60
|
+
* @param {string} aliasMatcher - The glob pattern to match Twig files.
|
|
61
|
+
* @returns {Object} An object containing Twig aliases.
|
|
47
62
|
*/
|
|
48
63
|
function getAliases(aliasMatcher) {
|
|
49
|
-
// Create default aliases
|
|
50
64
|
let aliases = {};
|
|
51
|
-
|
|
52
|
-
glob.sync(aliasMatcher).forEach((file) => {
|
|
65
|
+
globSync(aliasMatcher).forEach((file) => {
|
|
53
66
|
const filePath = file.split(`${srcDir}/`)[1];
|
|
54
|
-
const fileName =
|
|
55
|
-
|
|
67
|
+
const fileName = basename(filePath);
|
|
56
68
|
if (emulsifyConfig.project.platform === 'drupal') {
|
|
57
69
|
aliases[`${projectName}/${fileName.replace('.twig', '')}`] = file;
|
|
58
70
|
}
|
|
59
71
|
});
|
|
60
|
-
// Add typical @namespace (path to directory) aliases for twig partials.
|
|
61
72
|
const dirs = getDirectories(srcDir);
|
|
62
73
|
dirs.forEach((dir) => {
|
|
63
74
|
const name = cleanDirectoryName(dir);
|
|
64
75
|
Object.assign(aliases, {
|
|
65
|
-
[`@${name}`]: `${projectDir}/${
|
|
76
|
+
[`@${name}`]: `${projectDir}/${basename(srcDir)}/${dir}`,
|
|
66
77
|
});
|
|
67
78
|
});
|
|
68
|
-
|
|
69
79
|
return aliases;
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
// Alias twig namespaces.
|
|
73
82
|
const TwigResolve = {
|
|
74
83
|
extensions: ['.twig'],
|
|
75
84
|
alias: getAliases(aliasPattern),
|
|
76
85
|
};
|
|
77
86
|
|
|
78
|
-
|
|
79
|
-
TwigResolve,
|
|
80
|
-
};
|
|
87
|
+
export default { TwigResolve };
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A loader function that replaces occurrences of "projectName:" with "projectName/".
|
|
3
|
+
*
|
|
4
|
+
* @param {string} source - The source string to process.
|
|
5
|
+
* @returns {string} The transformed source.
|
|
6
|
+
*/
|
|
7
|
+
export default function (source) {
|
|
2
8
|
const projectName = this.getOptions().projectName || '';
|
|
9
|
+
/* eslint-disable security/detect-non-literal-regexp */
|
|
3
10
|
const result = source.replace(
|
|
4
11
|
new RegExp(`${projectName}:`, 'g'),
|
|
5
12
|
`${projectName}/`,
|
|
6
13
|
);
|
|
14
|
+
/* eslint-enable security/detect-non-literal-regexp */
|
|
7
15
|
return result;
|
|
8
|
-
}
|
|
16
|
+
}
|