b13-rocket 0.5.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/.editorconfig +102 -0
- package/.gitlab-ci.yml +14 -0
- package/.nvmrc +1 -0
- package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +541 -0
- package/.yarn/releases/yarn-3.2.3.cjs +783 -0
- package/.yarnrc.yml +5 -0
- package/README.md +98 -0
- package/eslintrc.json +207 -0
- package/files/.ddev/commands/web/rocket.sh +10 -0
- package/files/.ddev/docker-compose.webpack.yaml +9 -0
- package/index.js +4 -0
- package/package.json +71 -0
- package/src/build.js +158 -0
- package/src/cli.js +57 -0
- package/src/create.js +65 -0
- package/src/help.js +36 -0
- package/src/hmr.js +84 -0
- package/src/notification.js +55 -0
- package/src/scss/lessBuild.js +104 -0
- package/src/scss/scssBuild.js +186 -0
- package/src/sites.js +53 -0
- package/src/stylelint.js +33 -0
- package/src/utility/env.js +49 -0
- package/src/version.js +6 -0
- package/src/webpack/webpack.config.base.js +113 -0
- package/src/webpack/webpack.config.js +83 -0
package/src/help.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { Notification } from './notification';
|
|
3
|
+
import { sites } from './sites';
|
|
4
|
+
|
|
5
|
+
export async function help(args) {
|
|
6
|
+
const { siteConfig, hasSiteConfig } = await sites(args);
|
|
7
|
+
const siteOptions = `--site={ ${chalk.blackBright([...siteConfig.keys()].join(' | '))} }`;
|
|
8
|
+
const modeOptions = `--mode={ ${chalk.blackBright('development | production (default)')} }`;
|
|
9
|
+
|
|
10
|
+
const menus = {
|
|
11
|
+
main: `
|
|
12
|
+
${chalk.greenBright('rocket [command] <options>')}
|
|
13
|
+
${chalk.blueBright('build')} ..... build JS and CSS
|
|
14
|
+
${siteOptions}
|
|
15
|
+
${modeOptions}
|
|
16
|
+
--target={ ${chalk.blackBright('js | scss | less')} }
|
|
17
|
+
--bundleAnalyzer=true ${chalk.blackBright('// start "webpack-bundle-analyzer"')}
|
|
18
|
+
|
|
19
|
+
${chalk.blueBright('hmr')} ....... start hot module replacment
|
|
20
|
+
${siteOptions}
|
|
21
|
+
${modeOptions}
|
|
22
|
+
|
|
23
|
+
${chalk.blueBright('create')} .... create new config file for your TYPO3 site extension
|
|
24
|
+
|
|
25
|
+
${chalk.blueBright('stylelint')} ... format SCSS via stylelint
|
|
26
|
+
${siteOptions}
|
|
27
|
+
--fix={ false (default) | true }
|
|
28
|
+
|
|
29
|
+
${chalk.blueBright('help')} ...... show help menu for a command
|
|
30
|
+
|
|
31
|
+
${chalk.blueBright('version')} ... show package version
|
|
32
|
+
`,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
Notification.log(menus.main);
|
|
36
|
+
}
|
package/src/hmr.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { sites } from './sites';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import webpack from 'webpack';
|
|
4
|
+
import webpackDevServer from 'webpack-dev-server';
|
|
5
|
+
import { default as webpackConfig } from './webpack/webpack.config.base';
|
|
6
|
+
import { env } from './utility/env';
|
|
7
|
+
import { VueLoaderPlugin } from 'vue-loader';
|
|
8
|
+
import { Notification } from './notification';
|
|
9
|
+
|
|
10
|
+
// on exit
|
|
11
|
+
process.on('SIGINT', () => {
|
|
12
|
+
env.disableHMR();
|
|
13
|
+
Notification.log('🚀 hdgdl...');
|
|
14
|
+
process.exit();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export async function hmr(args) {
|
|
18
|
+
const { siteConfig, hasSiteConfig } = await sites(args);
|
|
19
|
+
|
|
20
|
+
if (!args.sitesConfigPath) {
|
|
21
|
+
console.log(error('missing site build config'));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const sitesConfigPath = args.sitesConfigPath;
|
|
26
|
+
|
|
27
|
+
if (hasSiteConfig(args.site)) {
|
|
28
|
+
const site = siteConfig.get(args.site);
|
|
29
|
+
const isDdev = process.env.DDEV_PRIMARY_URL;
|
|
30
|
+
const primaryUrl = new URL(isDdev ? process.env.DDEV_PRIMARY_URL : 'https://127.0.0.1');
|
|
31
|
+
const port = isDdev ? 8088 : 8080;
|
|
32
|
+
const wpConfig = webpackConfig(site.config, sitesConfigPath, 'development');
|
|
33
|
+
|
|
34
|
+
let publicPath = `http://127.0.0.1:${port}/`;
|
|
35
|
+
if (site.config.js.path.public) {
|
|
36
|
+
publicPath = `https://${primaryUrl.hostname}:${port}${site.config.js.path.public}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
wpConfig.output = {
|
|
40
|
+
filename: '[name]-hmr.js',
|
|
41
|
+
libraryTarget: 'umd',
|
|
42
|
+
library: site.config.js.filename.main,
|
|
43
|
+
umdNamedDefine: false,
|
|
44
|
+
path: path.resolve(__dirname, site.config.js.path.target),
|
|
45
|
+
publicPath,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
wpConfig.plugins.push(
|
|
49
|
+
new VueLoaderPlugin(),
|
|
50
|
+
new webpack.DefinePlugin({
|
|
51
|
+
B13_HMR_ENABLED: JSON.stringify(true),
|
|
52
|
+
}),
|
|
53
|
+
{
|
|
54
|
+
apply: (compiler) => {
|
|
55
|
+
compiler.hooks.watchRun.tap('enableHMR', (compilation) => {
|
|
56
|
+
env.enableHMR();
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const devServer = new webpackDevServer({
|
|
63
|
+
headers: { 'Access-Control-Allow-Origin': '*' },
|
|
64
|
+
historyApiFallback: true,
|
|
65
|
+
server: isDdev ? 'http' : 'https',
|
|
66
|
+
allowedHosts: 'all',
|
|
67
|
+
port,
|
|
68
|
+
hot: true,
|
|
69
|
+
client: {
|
|
70
|
+
webSocketURL: {
|
|
71
|
+
hostname: primaryUrl.hostname,
|
|
72
|
+
pathname: '/ws',
|
|
73
|
+
port,
|
|
74
|
+
protocol: 'wss',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
}, webpack(wpConfig));
|
|
78
|
+
|
|
79
|
+
(async() => {
|
|
80
|
+
await devServer.start();
|
|
81
|
+
console.log(`Webpack Dev Server running at https://${primaryUrl.hostname}:${port}`);
|
|
82
|
+
})();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
const bold = chalk.bold;
|
|
4
|
+
const success = chalk.bold.green;
|
|
5
|
+
const warn = chalk.bold.yellow;
|
|
6
|
+
const error = chalk.bold.red;
|
|
7
|
+
const blue = chalk.bold.blue;
|
|
8
|
+
|
|
9
|
+
export class Notification {
|
|
10
|
+
|
|
11
|
+
static log(msg = '') {
|
|
12
|
+
if (msg.length > 0) {
|
|
13
|
+
console.log(msg);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static bold(msg = '') {
|
|
18
|
+
if (msg.length > 0) {
|
|
19
|
+
console.log(bold(msg));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static warn(msg = '') {
|
|
24
|
+
if (msg.length > 0) {
|
|
25
|
+
console.warn(warn(`Warning: ${msg}`));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static success(msg = '') {
|
|
30
|
+
if (msg.length > 0) {
|
|
31
|
+
console.log(success(`Success: ${msg}`));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static error(msg = '', e) {
|
|
36
|
+
if (msg.length > 0) {
|
|
37
|
+
console.error(error(`Error: ${msg}`));
|
|
38
|
+
}
|
|
39
|
+
if (e) {
|
|
40
|
+
console.error(e);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static taskInfo(msg = '', siteName = '') {
|
|
45
|
+
if (msg.length > 0) {
|
|
46
|
+
let notification = `\n${msg}`;
|
|
47
|
+
if (siteName.length > 0) {
|
|
48
|
+
notification += ` // ${blue(siteName)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(notification);
|
|
52
|
+
console.log('------------');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { Notification } from '../notification';
|
|
3
|
+
import size from 'filesize';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import less from 'less';
|
|
7
|
+
|
|
8
|
+
const getDirName = path.dirname;
|
|
9
|
+
const success = chalk.bold.green;
|
|
10
|
+
|
|
11
|
+
const reflect = p => p.then(v => ({ v, status: 'fulfilled' }),
|
|
12
|
+
e => ({ e, status: 'rejected' }));
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* LESS build
|
|
16
|
+
*/
|
|
17
|
+
export class LessBuild {
|
|
18
|
+
|
|
19
|
+
constructor(_config = {}, _sitesConfigPath = './') {
|
|
20
|
+
this.config = _config;
|
|
21
|
+
this.sitesConfigPath = _sitesConfigPath;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* build
|
|
26
|
+
* process all LESS file
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
build() {
|
|
30
|
+
const fileProcessing = [];
|
|
31
|
+
for (const [dest, src] of Object.entries(this.config.less.files)) {
|
|
32
|
+
fileProcessing.push(this.processLessFile({
|
|
33
|
+
src: path.resolve(this.sitesConfigPath, src),
|
|
34
|
+
dest: path.resolve(this.sitesConfigPath, dest),
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// wait until all async processed less files are ready
|
|
39
|
+
Promise.all(fileProcessing.map(reflect)).then((results) => {
|
|
40
|
+
const success = results.filter(x => x.status === 'fulfilled');
|
|
41
|
+
Notification.taskInfo('Process LESS file(s)', this.config.name);
|
|
42
|
+
success.forEach(item => console.log(item.v));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* process less file
|
|
48
|
+
* @param {Object} options to process less file
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
51
|
+
processLessFile(options = {}) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const targetFileName = path.basename(options.dest);
|
|
54
|
+
|
|
55
|
+
// render the result
|
|
56
|
+
less.render(fs.readFileSync(options.src).toString(), {
|
|
57
|
+
filename: path.resolve(options.src),
|
|
58
|
+
compress: true,
|
|
59
|
+
}).then((result) => {
|
|
60
|
+
if (!fs.existsSync(getDirName(options.dest))) {
|
|
61
|
+
fs.mkdirSync(getDirName(options.dest));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// write the result to file
|
|
65
|
+
fs.writeFile(options.dest, result.css, (error) => {
|
|
66
|
+
if (error) {
|
|
67
|
+
Notification.error(`Can't write file: ${options.dest} error: ${error}`);
|
|
68
|
+
}
|
|
69
|
+
fs.stat(options.dest, (err, stats) => {
|
|
70
|
+
resolve(`${success(targetFileName)} ${size(stats.size)}`);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
(err) => {
|
|
75
|
+
Notification.error('less build failed', err);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* make sure CSS public directory exists and is clean
|
|
83
|
+
* @returns {void}
|
|
84
|
+
*/
|
|
85
|
+
clear() {
|
|
86
|
+
const cssPath = path.resolve(this.sitesConfigPath, this.config.less.path.target);
|
|
87
|
+
if (!fs.existsSync(cssPath)) {
|
|
88
|
+
fs.mkdirSync(cssPath);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
fs.readdirSync(cssPath).forEach((file) => {
|
|
92
|
+
|
|
93
|
+
// don't remove critical css files
|
|
94
|
+
if (!file.match(/critical-/g)) {
|
|
95
|
+
fs.unlinkSync(path.resolve(this.sitesConfigPath, `${this.config.less.path.target}/${file}`), (err) => {
|
|
96
|
+
if (err) {
|
|
97
|
+
Notification.error(err);
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { Notification } from '../notification';
|
|
3
|
+
import size from 'filesize';
|
|
4
|
+
import postcss from 'postcss';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import sass from 'sass';
|
|
8
|
+
import stylelint from 'stylelint';
|
|
9
|
+
import stylelintConfig from 'b13-stylelint-config';
|
|
10
|
+
|
|
11
|
+
const getDirName = path.dirname;
|
|
12
|
+
const success = chalk.bold.green;
|
|
13
|
+
const bold = chalk.bold;
|
|
14
|
+
|
|
15
|
+
const reflect = p => p.then(v => ({ v, status: 'fulfilled' }),
|
|
16
|
+
e => ({ e, status: 'rejected' }));
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* SCSS build
|
|
20
|
+
*/
|
|
21
|
+
export class ScssBuild {
|
|
22
|
+
|
|
23
|
+
constructor(_config = {}, _sitesConfigPath = './') {
|
|
24
|
+
this.config = _config;
|
|
25
|
+
this.sitesConfigPath = _sitesConfigPath;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* build
|
|
30
|
+
* process all SCSS file
|
|
31
|
+
* @returns {void}
|
|
32
|
+
*/
|
|
33
|
+
build() {
|
|
34
|
+
const fileProcessing = [];
|
|
35
|
+
for (const [dest, src] of Object.entries(this.config.scss.files)) {
|
|
36
|
+
fileProcessing.push(this.processScssFile({
|
|
37
|
+
src: path.resolve(this.sitesConfigPath, src),
|
|
38
|
+
dest: path.resolve(this.sitesConfigPath, dest),
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// wait until all async processed scss files are ready
|
|
43
|
+
Promise.all(fileProcessing.map(reflect)).then((results) => {
|
|
44
|
+
const success = results.filter(x => x.status === 'fulfilled');
|
|
45
|
+
Notification.taskInfo('Process SCSS file(s)', this.config.name);
|
|
46
|
+
success.forEach(item => console.log(item.v));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* stylelint fixer
|
|
52
|
+
*/
|
|
53
|
+
stylelintFixer(fix = false) {
|
|
54
|
+
const files = new Set();
|
|
55
|
+
Object.entries(this.config.scss.files).forEach((filePath) => {
|
|
56
|
+
const b = filePath[1].replace(path.basename(filePath[1]), '**/*.scss');
|
|
57
|
+
const c = path.resolve(this.sitesConfigPath, b);
|
|
58
|
+
files.add(c);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
Notification.log(`\n${bold(this.config.name)} scss file path(s):`);
|
|
62
|
+
[...files].forEach(file => Notification.log(`- ${file}`));
|
|
63
|
+
|
|
64
|
+
stylelint.lint({
|
|
65
|
+
files: [...files],
|
|
66
|
+
config: stylelintConfig,
|
|
67
|
+
fix,
|
|
68
|
+
})
|
|
69
|
+
.then((data) => {
|
|
70
|
+
|
|
71
|
+
if (data.errored) {
|
|
72
|
+
const resultsWithErrors = data.results.filter(item => item.errored);
|
|
73
|
+
Notification.log('\n');
|
|
74
|
+
Notification.error(`Found ${resultsWithErrors.length} stylelint errors!`);
|
|
75
|
+
resultsWithErrors.forEach(result => {
|
|
76
|
+
Notification.log('\n');
|
|
77
|
+
Notification.log(result.source);
|
|
78
|
+
result.warnings.forEach(warning => {
|
|
79
|
+
Notification.error(`- ${warning.text} at line: ${warning.line}`);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
Notification.log('\n');
|
|
85
|
+
Notification.error('stylelint failed');
|
|
86
|
+
} else {
|
|
87
|
+
Notification.log('\n');
|
|
88
|
+
Notification.success('stylelint successfull');
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
.catch((err) => {
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
Notification.log('\n');
|
|
94
|
+
Notification.error(`stylelint failed: ${err}`);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* process scss file
|
|
100
|
+
* @param {Object} options to process scss file
|
|
101
|
+
* @returns {void}
|
|
102
|
+
*/
|
|
103
|
+
processScssFile(options = {}) {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
const targetFileName = path.basename(options.dest);
|
|
106
|
+
|
|
107
|
+
// render the result
|
|
108
|
+
let result;
|
|
109
|
+
try {
|
|
110
|
+
result = sass.renderSync({
|
|
111
|
+
file: options.src,
|
|
112
|
+
outputStyle: options.style,
|
|
113
|
+
style: 'compressed',
|
|
114
|
+
});
|
|
115
|
+
} catch (e) {
|
|
116
|
+
Notification.error('scss build failed', e);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const postcssPlugins = [
|
|
121
|
+
require('autoprefixer')(),
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
// group media queries
|
|
125
|
+
if (this.config.scss.groupMediaQueries) {
|
|
126
|
+
postcssPlugins.push(require('css-mqpacker')());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// group media queries
|
|
130
|
+
if (this.config.scss.sortMediaQueries) {
|
|
131
|
+
postcssPlugins.push(require('postcss-sort-media-queries')());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
postcssPlugins.push(
|
|
135
|
+
require('cssnano')({ preset: 'default' }),
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// post process the css files
|
|
139
|
+
// see plugins and options: https://github.com/postcss/postcss
|
|
140
|
+
postcss(postcssPlugins).process(
|
|
141
|
+
result.css.toString(),
|
|
142
|
+
{ from: undefined },
|
|
143
|
+
).then(result => {
|
|
144
|
+
|
|
145
|
+
if (!fs.existsSync(getDirName(options.dest))) {
|
|
146
|
+
fs.mkdirSync(getDirName(options.dest), { recursive: true });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// write the result to file
|
|
150
|
+
fs.writeFile(options.dest, result.css, (error) => {
|
|
151
|
+
if (error) {
|
|
152
|
+
Notification.error(`Can't write file: ${options.dest} error: ${error}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
fs.stat(options.dest, (err, stats) => {
|
|
156
|
+
resolve(`${success(targetFileName)} ${size(stats.size)}`);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* make sure CSS public directory exists and is clean
|
|
165
|
+
* @returns {void}
|
|
166
|
+
*/
|
|
167
|
+
clear() {
|
|
168
|
+
const cssPath = path.resolve(this.sitesConfigPath, this.config.scss.path.target);
|
|
169
|
+
if (!fs.existsSync(cssPath)) {
|
|
170
|
+
fs.mkdirSync(cssPath, { recursive: true });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fs.readdirSync(cssPath).forEach((file) => {
|
|
174
|
+
|
|
175
|
+
// don't remove critical css files
|
|
176
|
+
if (!file.match(/critical-/g)) {
|
|
177
|
+
fs.unlinkSync(path.resolve(this.sitesConfigPath, `${this.config.scss.path.target}/${file}`), (err) => {
|
|
178
|
+
if (err) {
|
|
179
|
+
Notification.error(err);
|
|
180
|
+
throw err;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
package/src/sites.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { Notification } from './notification';
|
|
3
|
+
|
|
4
|
+
class Config {
|
|
5
|
+
constructor(config = {}, filename = '') {
|
|
6
|
+
this.config = config;
|
|
7
|
+
this.filename = filename;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
hasJs() {
|
|
11
|
+
return this.config.js && Object.keys(this.config.js).length > 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
hasScss() {
|
|
15
|
+
return this.config.scss && Object.keys(this.config.scss).length > 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
hasLess() {
|
|
19
|
+
return this.config.less && Object.keys(this.config.less).length > 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export async function sites(args) {
|
|
25
|
+
const sites = new Map();
|
|
26
|
+
const sitesConfigPath = args.sitesConfigPath;
|
|
27
|
+
|
|
28
|
+
if (!sitesConfigPath || !fs.existsSync(sitesConfigPath)) {
|
|
29
|
+
Notification.error('missing site build config');
|
|
30
|
+
return sites;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fs.readdirSync(
|
|
34
|
+
sitesConfigPath,
|
|
35
|
+
{ withFileTypes: true },
|
|
36
|
+
).forEach((file) => {
|
|
37
|
+
if (file.name.match(/(site_|\.package)/)) {
|
|
38
|
+
const config = require(`${sitesConfigPath}/${file.name}`);
|
|
39
|
+
sites.set(config.name, new Config(config, file.name));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const hasSiteConfig = (name = '') => {
|
|
44
|
+
if (!sites.has(name)) {
|
|
45
|
+
Notification.error(`Site ${name} not found`);
|
|
46
|
+
const availableSitesOptions = Array.from(sites.keys()).join(', ');
|
|
47
|
+
Notification.bold(`Available options: ${availableSitesOptions}`);
|
|
48
|
+
}
|
|
49
|
+
return sites.has(name);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { siteConfig: sites, hasSiteConfig };
|
|
53
|
+
}
|
package/src/stylelint.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sites } from './sites';
|
|
2
|
+
import { ScssBuild } from './scss/scssBuild';
|
|
3
|
+
import { Notification } from './notification';
|
|
4
|
+
|
|
5
|
+
export async function stylelint(args) {
|
|
6
|
+
const { siteConfig, hasSiteConfig } = await sites(args);
|
|
7
|
+
|
|
8
|
+
if (!args.sitesConfigPath) {
|
|
9
|
+
Notification.error('missing site build config');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const sitesConfigPath = args.sitesConfigPath;
|
|
14
|
+
const fix = args.fix === 'true';
|
|
15
|
+
|
|
16
|
+
Notification.taskInfo('Run Stylelint fixer');
|
|
17
|
+
|
|
18
|
+
if (args.site && siteConfig.has(args.site)) {
|
|
19
|
+
|
|
20
|
+
// cleanup single site
|
|
21
|
+
const site = siteConfig.get(args.site);
|
|
22
|
+
const scss = new ScssBuild(site.config, sitesConfigPath);
|
|
23
|
+
scss.stylelintFixer(fix);
|
|
24
|
+
|
|
25
|
+
} else {
|
|
26
|
+
|
|
27
|
+
// cleanup all sites
|
|
28
|
+
for (const [key, value] of siteConfig.entries()) {
|
|
29
|
+
const scss = new ScssBuild(value.config, sitesConfigPath);
|
|
30
|
+
scss.stylelintFixer(fix);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { Notification } from './../notification';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
const ENV_FILE_PATH = path.resolve(__dirname, './../../../../../../.env');
|
|
6
|
+
const ENV_HRM_VAR_NAME = 'TYPO3_TS_ENABLE_HMR';
|
|
7
|
+
const ENV_HRM_VAR_NAME_REGEX = new RegExp(/TYPO3_TS_ENABLE_HMR="[0,1]"/, 'g');
|
|
8
|
+
|
|
9
|
+
class ENV {
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.hasEnv = fs.existsSync(ENV_FILE_PATH);
|
|
13
|
+
if (!this.hasEnv) {
|
|
14
|
+
Notification.warn(`No .env file found at: ${ENV_FILE_PATH}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
enableHMR() {
|
|
19
|
+
if (this.hasEnv) {
|
|
20
|
+
let content = this.getContent();
|
|
21
|
+
if (!content.match(ENV_HRM_VAR_NAME_REGEX)) {
|
|
22
|
+
content += `
|
|
23
|
+
${ENV_HRM_VAR_NAME}="1"
|
|
24
|
+
`;
|
|
25
|
+
}
|
|
26
|
+
this.updateFile(content.replace(ENV_HRM_VAR_NAME_REGEX, `${ENV_HRM_VAR_NAME}="1"`));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
disableHMR() {
|
|
31
|
+
if (this.hasEnv) {
|
|
32
|
+
const content = this.getContent();
|
|
33
|
+
this.updateFile(content.replace(ENV_HRM_VAR_NAME_REGEX, `${ENV_HRM_VAR_NAME}="0"`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
updateFile(content = '') {
|
|
38
|
+
if (content.length > 0) {
|
|
39
|
+
fs.writeFileSync(ENV_FILE_PATH, content);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getContent() {
|
|
44
|
+
return fs.readFileSync(ENV_FILE_PATH, 'utf8');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const env = new ENV();
|
|
49
|
+
export { env };
|