b13-rocket 0.7.2 → 0.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/src/help.js CHANGED
@@ -1,34 +1,24 @@
1
+ import { Notification } from "./notification.js";
1
2
  import chalk from 'chalk';
2
- import { Notification } from './notification';
3
- import { sites } from './sites';
4
3
 
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)')} }`;
4
+ export async function help(sitesConfig, args) {
5
+ const siteOptions = `--site={ ${sitesConfig.availableSite.join(', ')} }`;
6
+ const modeOptions = `--mode={ 'development | production (default)' } }`;
9
7
 
10
8
  const menus = {
11
9
  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"')}
10
+ ${chalk.greenBright('rocket [command] <options>')}
11
+ ${chalk.blueBright('build')} ..... build JS and CSS
12
+ ${siteOptions}
13
+ ${modeOptions}
18
14
 
19
- ${chalk.blueBright('hmr')} ....... start hot module replacment
20
- ${siteOptions}
21
- ${modeOptions}
15
+ ${chalk.blueBright('hmr')} ....... start hot module replacment
16
+ ${siteOptions}
17
+ ${modeOptions}
22
18
 
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 }
19
+ ${chalk.blueBright('help')} ...... show help menu for a command
28
20
 
29
- ${chalk.blueBright('help')} ...... show help menu for a command
30
-
31
- ${chalk.blueBright('version')} ... show package version
21
+ ${chalk.blueBright('version')} ... show package version
32
22
  `,
33
23
  };
34
24
 
package/src/sites.js CHANGED
@@ -1,53 +1,46 @@
1
- import fs from 'fs';
2
- import { Notification } from './notification';
1
+ import { readdir } from 'node:fs/promises';
2
+ import { Notification } from './notification.js';
3
+ import path from 'node:path';
3
4
 
4
5
  class Config {
5
6
  constructor(config = {}, filename = '') {
6
7
  this.config = config;
8
+ this.config.input = path.resolve(process.env.INIT_CWD, this.config.input);
9
+ this.config.output = path.resolve(process.env.INIT_CWD, this.config.output);
10
+ this.config.root = path.resolve(process.env.INIT_CWD, this.config.root);
7
11
  this.filename = filename;
8
12
  }
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
13
  }
22
14
 
23
-
24
15
  export async function sites(args) {
25
16
  const sites = new Map();
17
+ let sharedConfig = {};
26
18
  const sitesConfigPath = args.sitesConfigPath;
27
19
 
28
- if (!sitesConfigPath || !fs.existsSync(sitesConfigPath)) {
20
+ if (!sitesConfigPath) {
29
21
  Notification.error('missing site build config');
30
22
  return sites;
31
23
  }
32
24
 
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}`);
25
+ try {
26
+ const files = await readdir(sitesConfigPath);
27
+ for (const file of files) {
28
+ if (file.match(/(site_|\.package)/)) {
29
+ const { config } = await import(`${sitesConfigPath}/${file}`);
30
+ sites.set(config.name, new Config(config, file));
31
+ }
32
+ if (file.match(/shared\.vite\.config/)) {
33
+ const { sharedViteConfig } = await import(`${sitesConfigPath}/${file}`);
34
+ sharedConfig = sharedViteConfig;
35
+ }
48
36
  }
49
- return sites.has(name);
37
+ } catch (err) {
38
+ console.error(err);
50
39
  }
51
40
 
52
- return { siteConfig: sites, hasSiteConfig };
41
+ return {
42
+ sites,
43
+ availableSite: [...sites.keys()],
44
+ sharedConfig,
45
+ };
53
46
  }
package/src/version.js CHANGED
@@ -1,6 +1,9 @@
1
- import { Notification } from './notification';
1
+ import { readFile } from 'node:fs/promises';
2
2
 
3
- export async function version() {
4
- const packagejson = require('./../package.json');
5
- Notification.log(packagejson.version);
6
- }
3
+ const json = JSON.parse(
4
+ await readFile(
5
+ new URL('./../package.json', import.meta.url)
6
+ )
7
+ );
8
+
9
+ export const version = json.version;
@@ -0,0 +1,56 @@
1
+ import { defineConfig } from 'vite';
2
+ import autoprefixer from 'autoprefixer';
3
+ import postcssSortMediaQueries from 'postcss-sort-media-queries';
4
+ import nested from 'postcss-nested';
5
+ import postcssPresetEnv from 'postcss-preset-env';
6
+ import svgLoader from 'vite-svg-loader';
7
+ import { ViteImageOptimizer } from 'vite-plugin-image-optimizer';
8
+ import path from 'node:path';
9
+
10
+ const mode = 'production';
11
+
12
+ export const defaultViteConfig = defineConfig({
13
+ mode,
14
+ publicDir: false,
15
+ base: './',
16
+ build: {
17
+ manifest: true,
18
+ target: 'es2020',
19
+ rollupOptions: {
20
+ output: {
21
+ entryFileNames: '[name].js',
22
+ assetFileNames: '[name][extname]',
23
+ manualChunks: (chunkPath) => {
24
+ const fileName = path.basename(chunkPath);
25
+ // add extra chunck for all css files starting with 'external-'
26
+ if (fileName.startsWith('external-') && fileName.match(/\.(css|scss)$/)) {
27
+ return fileName;
28
+ }
29
+ return chunkPath.split('/').reverse()[chunkPath.split('/').reverse().indexOf('node_modules') - 1];
30
+ },
31
+ },
32
+ },
33
+ },
34
+ plugins: [
35
+ svgLoader({
36
+ defaultImport: 'url',
37
+ }),
38
+ ViteImageOptimizer(),
39
+ ],
40
+ css: {
41
+ devSourcemap: true,
42
+ postcss: {
43
+ plugins: [
44
+ nested(),
45
+ postcssPresetEnv(),
46
+ postcssSortMediaQueries(),
47
+ autoprefixer({}),
48
+ ],
49
+ },
50
+ preprocessorOptions: {
51
+ scss: {
52
+ additionalData: `$mode: ${mode};`,
53
+ },
54
+ },
55
+ },
56
+ });
package/src/create.js DELETED
@@ -1,65 +0,0 @@
1
- import fs from 'fs';
2
- import { Notification } from './notification';
3
- import path from 'path';
4
- import readline from 'readline';
5
-
6
- // config file
7
- const CONFIG_TEMPLATE = (CONFIG_EXT_NAME = '', CONFIG_PKG_NAME = '') => `const relExtPath = '../../../web/typo3conf/ext/${CONFIG_EXT_NAME}';
8
- const relScssPath = \`\${relExtPath}/Resources/Private/Scss\`;
9
- const relCssPath = \`\${relExtPath}/Resources/Public/Css\`;
10
-
11
- module.exports = {
12
- name: '${CONFIG_PKG_NAME}',
13
- scss: {
14
- path: {
15
- source: relScssPath,
16
- target: relCssPath
17
- },
18
- // target (CSS) : source (SCSS)
19
- files: {
20
- [\`\${relCssPath}/main.css\`]: \`\${relScssPath}/main.scss\`
21
- }
22
- },
23
- js: {
24
- path: {
25
- source: \`\${relExtPath}/Resources/Private/JavaScript\`,
26
- target: \`\${relExtPath}/Resources/Public/JavaScript\`,
27
- public: '/typo3conf/ext/YOUR_EXT_NAME/Resources/Public/JavaScript/'
28
- },
29
- filename: {
30
- main : 'main',
31
- commonVendor: 'common-vendor'
32
- }
33
- }
34
- };
35
- `;
36
-
37
- class Create {
38
- static createConfigFile(sitesConfigPath = './', fileName = 'demo.package.js', extName = '', pkgName = '') {
39
- const newConfigFile = path.resolve(sitesConfigPath, fileName);
40
- fs.writeFile(newConfigFile, CONFIG_TEMPLATE(extName, pkgName), (error) => {
41
- if (error) {
42
- Notification.error(`Can't write file: ${newConfigFile} error: ${error}`);
43
- }
44
-
45
- Notification.success(` new ${newConfigFile} config created`);
46
- });
47
- }
48
- }
49
-
50
- export async function create(args) {
51
-
52
- const rl = readline.createInterface({
53
- input: process.stdin,
54
- output: process.stdout,
55
- });
56
-
57
- Notification.taskInfo('Create new config file for your TYPO3 site extension');
58
-
59
- rl.question('TYPO3 extension name (/typo3conf/ext/)? i.e. "site_demo": ', (extName) => {
60
- rl.question('Package Name? i.e. "demo": ', (pkgName) => {
61
- Create.createConfigFile(args.sitesConfigPath, `${pkgName}.package.js`, extName, pkgName);
62
- rl.close();
63
- });
64
- });
65
- }
package/src/hmr.js DELETED
@@ -1,84 +0,0 @@
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
- }
@@ -1,104 +0,0 @@
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
- }
@@ -1,186 +0,0 @@
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/stylelint.js DELETED
@@ -1,33 +0,0 @@
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
- }