1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.getBabelOptions = exports.getBabelConfig = exports.getPostCssConfigSync = exports.getPostCssConfig = void 0;
|
4 | const tslib_1 = require("tslib");
|
5 | const cli_utils_1 = require("@design-systems/cli-utils");
|
6 | const tapable_1 = require("tapable");
|
7 | const path_1 = tslib_1.__importDefault(require("path"));
|
8 | const dedent_1 = tslib_1.__importDefault(require("dedent"));
|
9 | const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
10 | const minimatch_1 = tslib_1.__importDefault(require("minimatch"));
|
11 | const chokidar_1 = require("chokidar");
|
12 | const fast_glob_1 = tslib_1.__importDefault(require("fast-glob"));
|
13 | const pretty_bytes_1 = tslib_1.__importDefault(require("pretty-bytes"));
|
14 | const pretty_ms_1 = tslib_1.__importDefault(require("pretty-ms"));
|
15 | const clean_css_1 = tslib_1.__importDefault(require("clean-css"));
|
16 | const utils_1 = require("./utils");
|
17 | const babel_1 = tslib_1.__importStar(require("./babel"));
|
18 | const postcss_1 = tslib_1.__importStar(require("./postcss"));
|
19 | const command_1 = require("./command");
|
20 | const typescript_1 = tslib_1.__importDefault(require("./typescript"));
|
21 | const POSTCSS_CONFIG = path_1.default.join(__dirname, './configs/postcss.config.js');
|
22 |
|
23 | function match(file, globs) {
|
24 | const resolved = path_1.default.resolve(file);
|
25 | const globArr = Array.isArray(globs) ? globs : [globs];
|
26 | return globArr.find(currGlob => minimatch_1.default(resolved, currGlob));
|
27 | }
|
28 | var postcss_2 = require("./postcss");
|
29 | Object.defineProperty(exports, "getPostCssConfig", { enumerable: true, get: function () { return postcss_2.getPostCssConfig; } });
|
30 | Object.defineProperty(exports, "getPostCssConfigSync", { enumerable: true, get: function () { return postcss_2.getPostCssConfigSync; } });
|
31 | var babel_2 = require("./babel");
|
32 | Object.defineProperty(exports, "getBabelConfig", { enumerable: true, get: function () { return babel_2.getBabelConfig; } });
|
33 | Object.defineProperty(exports, "getBabelOptions", { enumerable: true, get: function () { return babel_2.getBabelOptions; } });
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | class BuildPlugin {
|
42 | constructor() {
|
43 | this.hooks = {
|
44 | processCSSFiles: new tapable_1.SyncBailHook(['cssFiles'])
|
45 | };
|
46 | this.logger = cli_utils_1.createLogger({ scope: 'build' });
|
47 | this.cssFiles = new Map();
|
48 | this.buildArgs = command_1.defaults;
|
49 |
|
50 | this.generateCSS = async () => {
|
51 | if (this.cssFiles.size === 0) {
|
52 | return;
|
53 | }
|
54 | this.logger.trace('Generating merged css...');
|
55 | const outFile = path_1.default.join(this.buildArgs.outputDirectory, this.buildArgs.cssMain);
|
56 | const clean = new clean_css_1.default({
|
57 | sourceMap: true,
|
58 | sourceMapInlineSources: true,
|
59 | level: 2,
|
60 | rebase: false,
|
61 | rebaseTo: this.buildArgs.outputDirectory
|
62 | });
|
63 | const cssToMinify = Array.from(this.cssFiles.entries()).map(([file, { css, map }]) => ({
|
64 | [file]: {
|
65 | styles: css,
|
66 | sourceMap: map
|
67 | .toString()
|
68 | .replace(/<input css (\d+)>/g, '../src/<input css $1>')
|
69 | }
|
70 | }));
|
71 | const minified = clean.minify(this.hooks.processCSSFiles.call(cssToMinify) || cssToMinify);
|
72 | if (minified.errors.length > 0) {
|
73 | this.logger.debug('Errors\n\n', minified.errors.join('\n '), '\n');
|
74 | }
|
75 | if (minified.warnings.length > 0) {
|
76 | this.logger.debug('Warnings\n\n', minified.warnings.join('\n '), '\n');
|
77 | }
|
78 | const duration = pretty_ms_1.default(minified.stats.timeSpent);
|
79 | const efficiency = minified.stats.efficiency.toPrecision(2);
|
80 | const difference = pretty_bytes_1.default(minified.stats.originalSize - minified.stats.minifiedSize);
|
81 | this.logger.debug(dedent_1.default `
|
82 | CSS minification results:
|
83 |
|
84 | Original: ${pretty_bytes_1.default(minified.stats.originalSize)}
|
85 | Minified: ${pretty_bytes_1.default(minified.stats.minifiedSize)}
|
86 |
|
87 | ${difference} -${efficiency}% ${duration}\n
|
88 | `);
|
89 | await fs_extra_1.default.outputFile(`${outFile}.map`, minified.sourceMap);
|
90 | await fs_extra_1.default.outputFile(outFile, `${minified.styles}\n/*# sourceMappingURL=${this.buildArgs.cssMain}.map */`);
|
91 | this.logger.complete('Generated merged css');
|
92 | };
|
93 |
|
94 | this.getFileList = async () => fast_glob_1.default(`${this.buildArgs.inputDirectory}/**/*.*`);
|
95 |
|
96 | this.transformFile = async (file) => {
|
97 | const { inputDirectory, outputDirectory } = this.buildArgs;
|
98 | if (this.isIgnored(file)) {
|
99 | return;
|
100 | }
|
101 | switch (path_1.default.extname(file)) {
|
102 | case '.css':
|
103 | this.logger.pending(`CSS -> ${file}`);
|
104 | return (postcss_1.default({
|
105 | inFile: file,
|
106 | inDir: inputDirectory,
|
107 | outDir: outputDirectory,
|
108 | configFile: POSTCSS_CONFIG,
|
109 | watch: this.buildArgs.watch
|
110 | })
|
111 |
|
112 | .then(css => css && this.cssFiles.set(path_1.default.resolve(file), css)));
|
113 | case '.js':
|
114 | case '.jsx':
|
115 | case '.ts':
|
116 | case '.tsx':
|
117 | this.logger.pending(`transpiling -> ${file}`);
|
118 | return babel_1.default(file, inputDirectory, outputDirectory, babel_1.BABEL_CONFIG);
|
119 | default:
|
120 | this.logger.pending(`copy -> ${file}`);
|
121 | await fs_extra_1.default.copy(file, path_1.default.join(utils_1.getOutPath(inputDirectory, file, path_1.default.join(outputDirectory, 'cjs')), path_1.default.basename(file)));
|
122 | await fs_extra_1.default.copy(file, path_1.default.join(utils_1.getOutPath(inputDirectory, file, path_1.default.join(outputDirectory, 'esm')), path_1.default.basename(file)));
|
123 | break;
|
124 | }
|
125 | };
|
126 |
|
127 | this.onAddOrChanged = async (file) => {
|
128 | this.logger.info(`File changed: ${file}`);
|
129 | const extname = path_1.default.extname(file);
|
130 | try {
|
131 | const result = await this.transformFile(file);
|
132 | if (result &&
|
133 | 'success' in result &&
|
134 | result.success &&
|
135 | (extname === '.ts' || extname === '.tsx')) {
|
136 | await this.typescriptCompiler.buildTypes(true);
|
137 | }
|
138 | }
|
139 | catch (error) {
|
140 | this.logger.trace(error);
|
141 | }
|
142 | if (file.includes('theme.')) {
|
143 | await Promise.all([...this.cssFiles.keys()].map(async (cssFile) => this.transformFile(cssFile)));
|
144 | }
|
145 | if (extname === '.css' || file.includes('theme.')) {
|
146 | await this.generateCSS();
|
147 | await this.typescriptCompiler.buildTypes(true);
|
148 | }
|
149 | if (file.includes('tsconfig.json')) {
|
150 | await this.typescriptCompiler.buildTypes(true);
|
151 | }
|
152 | this.logger.watch('Watching for changes');
|
153 | };
|
154 |
|
155 | this.onDelete = async (file) => {
|
156 | const { inputDirectory, outputDirectory } = this.buildArgs;
|
157 | this.logger.info(`File deleted: ${file}`);
|
158 | switch (path_1.default.extname(file)) {
|
159 | case '.css': {
|
160 | const fName = path_1.default.resolve(file);
|
161 | if (this.cssFiles.has(fName)) {
|
162 | this.cssFiles.delete(fName);
|
163 | }
|
164 | const cssPath = postcss_1.getCSSPath(file, inputDirectory, outputDirectory);
|
165 | await Promise.all([
|
166 | this.generateCSS(),
|
167 | fs_extra_1.default.remove(cssPath),
|
168 | fs_extra_1.default.remove(`${cssPath}.json`)
|
169 | ]);
|
170 | return;
|
171 | }
|
172 | case '.js':
|
173 | case '.jsx':
|
174 | case '.ts':
|
175 | case '.tsx': {
|
176 | const { cjsFile, mjsFile } = babel_1.getJSOutputFiles(file, inputDirectory, outputDirectory);
|
177 | await Promise.all([fs_extra_1.default.remove(cjsFile), fs_extra_1.default.remove(mjsFile)]);
|
178 | return;
|
179 | }
|
180 | default:
|
181 | await fs_extra_1.default.remove(path_1.default.join(utils_1.getOutPath(inputDirectory, file, outputDirectory), path_1.default.basename(file)));
|
182 | }
|
183 | };
|
184 |
|
185 | this.isIgnored = (filename) => match(filename, this.buildArgs.ignore);
|
186 |
|
187 | this.watch = async () => {
|
188 | const watcher = chokidar_1.watch([this.buildArgs.inputDirectory, 'tsconfig.json'], {
|
189 | awaitWriteFinish: true,
|
190 | ignoreInitial: false
|
191 | });
|
192 |
|
193 | const withIgnore = (fn) => (file) => {
|
194 | if (this.isIgnored(file)) {
|
195 | return;
|
196 | }
|
197 | fn(file);
|
198 | };
|
199 | return new Promise((resolve, reject) => {
|
200 | watcher.on('error', reject);
|
201 | watcher.on('ready', () => {
|
202 | this.logger.watch('Watching for changes');
|
203 | watcher.on('add', withIgnore(this.onAddOrChanged));
|
204 | watcher.on('change', withIgnore(this.onAddOrChanged));
|
205 | watcher.on('unlink', withIgnore(this.onDelete));
|
206 | });
|
207 | });
|
208 | };
|
209 | }
|
210 |
|
211 | async run(args) {
|
212 | this.buildArgs = Object.assign(Object.assign({}, this.buildArgs), args);
|
213 | this.typescriptCompiler = new typescript_1.default(this.buildArgs);
|
214 | const startTime = Date.now();
|
215 | const { watch, outputDirectory } = this.buildArgs;
|
216 |
|
217 |
|
218 | if (!watch && !fs_extra_1.default.existsSync(outputDirectory)) {
|
219 | await fs_extra_1.default.remove('tsconfig.tsbuildinfo');
|
220 | }
|
221 | const files = await this.getFileList();
|
222 |
|
223 | const transformed = files
|
224 | .map(nextFile => !this.isIgnored(nextFile) && this.transformFile(nextFile))
|
225 | .filter((i) => typeof i !== 'boolean');
|
226 |
|
227 | const results = await Promise.all(transformed);
|
228 | await this.generateCSS();
|
229 | const hasTSFiles = files.some(file => path_1.default.extname(file).startsWith('.ts'));
|
230 | if (hasTSFiles) {
|
231 | if (results.find(result => result && result.success !== undefined && !result.success)) {
|
232 | this.logger.info('Skipping type build. Build errors found.');
|
233 | }
|
234 | else {
|
235 | await this.typescriptCompiler.buildTypes(watch);
|
236 | }
|
237 | }
|
238 | else {
|
239 | this.logger.info('Skipping type build. No .ts(x) files found.');
|
240 | }
|
241 | const runtime = pretty_ms_1.default(Date.now() - startTime);
|
242 | this.logger.done(`Transformed ${transformed.length} files in ${runtime} seconds`);
|
243 | if (watch) {
|
244 | return this.watch();
|
245 | }
|
246 | }
|
247 | }
|
248 | exports.default = BuildPlugin;
|
249 |
|
\ | No newline at end of file |