@cmmn/tools 1.8.6 → 1.9.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/bundle/bundle.js CHANGED
@@ -1,6 +1,6 @@
1
- import {rollup, watch} from "rollup";
1
+ import esbuild from "esbuild";
2
2
  import {getConfigOptions} from "./getConfigs.js";
3
- import {ConfigCreator} from "./rollup.config.js";
3
+ import {ConfigCreator} from "./esbuild.config.js";
4
4
  import fs from "fs";
5
5
  import path, {relative} from "path";
6
6
 
@@ -13,22 +13,16 @@ export async function bundle(...options) {
13
13
  stats: options.includes('--stats'),
14
14
  });
15
15
  const configs = configOptions.flatMap(x => new ConfigCreator(x).getConfig());
16
- if (!options.includes('--watch')) {
17
- for (let config of configs) {
18
- for (let key in config.input){
19
- // console.log(`1. ${key} (${config.input[key]})`);
20
- }
21
- const build = await rollup(config);
22
- for (let out of config.output){
23
- const file = path.join(out.dir, out.entryFileNames.replace('[name]', Object.keys(config.input)[0]));
24
- await build.write(out);
25
- const stat = fs.existsSync(file) ? fs.statSync(file) : 0;
26
- console.log(`SUCCESS: ${file} (${(stat.size/1024).toFixed(0)} Kb)`);
16
+ const contexts = await Promise.all(configs.map(x => esbuild.context(x)));
27
17
 
28
- }
18
+ if (options.includes('--watch')) {
19
+ for (let context of contexts) {
20
+ await context.watch({});
29
21
  }
30
22
  }else {
31
- await runWatching(configs)
23
+ for (let context of contexts) {
24
+ await context.dispose();
25
+ }
32
26
  }
33
27
  }
34
28
 
@@ -0,0 +1,338 @@
1
+ import { htmlPlugin } from '@craftamap/esbuild-plugin-html';
2
+ import path, {join} from "path";
3
+ import {readFileSync} from "node:fs";
4
+ import { lessLoader } from 'esbuild-plugin-less';
5
+
6
+ /**
7
+ * @typedef {import(rollup).RollupOptions} RollupOptions
8
+ * @typedef {import(rollup).OutputOptions} OutputOptions
9
+ */
10
+
11
+ export class ConfigCreator {
12
+
13
+ /**
14
+ * @type {{
15
+ * minify: boolean,
16
+ * input: string,
17
+ * devServer: boolean,
18
+ * module: string,
19
+ * external: string[],
20
+ * stats: boolean,
21
+ * name: string,
22
+ * styles: 'modules' | null,
23
+ * outDir: string,
24
+ * html: string,
25
+ * browser: boolean,
26
+ * dedupe: string[],
27
+ * target: string
28
+ * inject: string
29
+ * }}
30
+ */
31
+ options;
32
+
33
+ /**
34
+ * @type {string}
35
+ */
36
+ root = process.cwd();
37
+
38
+
39
+ constructor(options) {
40
+ this.options = {
41
+ module: 'es',
42
+ external: [],
43
+ name: 'index',
44
+ outDir: 'dist/bundle',
45
+ ...options
46
+ };
47
+ if (options.rootDir)
48
+ this.root = options.rootDir;
49
+ }
50
+
51
+ get outDir() {
52
+ return path.join(this.root, this.options.outDir);
53
+ }
54
+
55
+ getOutputFileName(module, minify) {
56
+ switch (module) {
57
+ case "cjs":
58
+ return `[name]${minify ? '.min' : ''}.cjs`;
59
+ case "es":
60
+ return `[name]${minify ? '.min' : ''}.js`;
61
+ default:
62
+ return `[name]-${module}${minify ? '.min' : ''}.js`;
63
+ }
64
+ }
65
+ //
66
+ // /**
67
+ // *
68
+ // * @returns {OutputOptions}
69
+ // */
70
+ // get output() {
71
+ // // const output = `${this.options.name ?? 'index'}-${this.options.module}${this.options.minify ? '.min' : ''}.js`;
72
+ // return this.options.module.split(',').map(module => ({
73
+ // entryFileNames: this.getOutputFileName(module, this.options.minify),
74
+ // // file: output,
75
+ // dir: this.outDir,
76
+ // sourcemap: this.options.minify ? true : 'inline',
77
+ // format: module,
78
+ // globals: module === 'umd' ? (Array.isArray(this.options.external) ? Object.fromEntries(this.options.external.map(x => [x, x])) : this.options.external) : [],
79
+ // assetFileNames: "assets/[name][extname]",
80
+ // chunkFileNames: "chunks/[name].js",
81
+ // name: this.options.global ?? 'global',
82
+ // sourcemapPathTransform: sourceMap => {
83
+ // const p = path.relative(this.root, path.resolve(this.outDir, sourceMap));
84
+ // return path.join('/', this.options.package, p);
85
+ // }
86
+ // }));
87
+ // }
88
+ //
89
+ // get html() {
90
+ // return html({
91
+ // publicPath: '/',
92
+ // dir: this.outDir,
93
+ // inject: false,
94
+ // template: (x) => {
95
+ // let inject = (this.options.inject === "json") ? `<script>
96
+ // globalThis.assets = ${JSON.stringify(Object.keys(x.bundle.bundle))};
97
+ // </script>`: Object.keys(x.bundle.bundle).map(key => {
98
+ // if (key.endsWith('css'))
99
+ // return `<link rel="stylesheet" href="${this.options.base ?? ''}/${key}" >`;
100
+ // if (key.endsWith('js'))
101
+ // return `<script type="module" defer src="${this.options.base ?? ''}/${key}"></script>`;
102
+ // }).join('\n');
103
+ // if (!this.options.minify) {
104
+ // const importMaps = Object.fromEntries(this.options.external
105
+ // .map(key => key.replace('.*', '/'))
106
+ // .map(key => [key, `/external/${this.options.alias?.[key] ?? key}`]));
107
+ // inject = `<script type="importmap" >${JSON.stringify({
108
+ // imports: importMaps
109
+ // })}</script>` + inject;
110
+ // }
111
+ // if (this.options.base){
112
+ // inject = `<base href="${this.options.base}">` + inject;
113
+ // }
114
+ // const html = fs.readFileSync(path.join(this.root, this.options.html), 'utf8')
115
+ // const interpolated = new Function(...Object.keys(this.options), `return \`${html}\`;`)(...Object.values(this.options));
116
+ // return interpolated.replace('</head>', inject + '</head>');
117
+ // }
118
+ // });
119
+ // }
120
+ //
121
+ // get devServer() {
122
+ // return serve({
123
+ // open: false,
124
+ // contentBase: [this.outDir, path.join(this.root, 'assets')],
125
+ // port: this.options.port,
126
+ // historyApiFallback: true
127
+ // });
128
+ // }
129
+ //
130
+ // get livereload() {
131
+ // return livereload({
132
+ // watch: [this.outDir, path.join(this.root, 'assets'), this.options.html],
133
+ // verbose: false, // Disable console output
134
+ // // other livereload options
135
+ // port: 12345,
136
+ // delay: 300,
137
+ // })
138
+ // }
139
+ //
140
+ // get visualizer() {
141
+ // return visualizer({
142
+ // open: true,
143
+ // sourcemap: true,
144
+ // template: 'treemap',
145
+ // brotliSize: true,
146
+ //
147
+ // filename: path.join(this.outDir, '/stats.html')
148
+ // })
149
+ // }
150
+ //
151
+ // get plugins() {
152
+ // const result = [
153
+ // replace({
154
+ // 'process.env.NODE_ENV': JSON.stringify(this.options.minify ? 'production' : 'development'),
155
+ // preventAssignment: true
156
+ // }),
157
+ // ...Styles(this),
158
+ // commonjs({
159
+ // requireReturnsDefault: "namespace",
160
+ // transformMixedEsModules: true,
161
+ // defaultIsModuleExports: true,
162
+ // }),
163
+ // nodeResolve({
164
+ // browser: this.options.browser,
165
+ // dedupe: this.options.dedupe || [],
166
+ // preferBuiltins: !this.options.browser
167
+ // }),
168
+ // sourcemaps(),
169
+ // builtins(),
170
+ // /*this.options.styles === 'modules' ? postCSS({
171
+ // mode: [
172
+ // "inject",
173
+ // {container: "head", prepend: true, attributes: {id: "global"}},
174
+ // ],
175
+ // plugins: [
176
+ // flexbugs,
177
+ // ],
178
+ // modules: {
179
+ // root: ''
180
+ // },
181
+ // namedExports: false,
182
+ // autoModules: true,
183
+ // }) : */
184
+ // Styles(this),
185
+ //
186
+ // string({
187
+ // include: /\.(html|svg|less)$/,
188
+ // exclude: /\.module\.css/
189
+ // }),
190
+ // json(),
191
+ //
192
+ // ];
193
+ // if (this.options.alias) {
194
+ // result.unshift(alias({
195
+ // entries: Object.entries(this.options.alias).map(([key, value]) => ({
196
+ // find: key,
197
+ // replacement: value.replace('<root>', this.root)
198
+ // }))
199
+ // }));
200
+ // }
201
+ // if (this.options.minify && this.options.mount){
202
+ // const toCopy = Object.entries(this.options.mount).map(([to, from]) => {
203
+ // return {src: from + '/*', dest: join(this.outDir, to)}
204
+ // });
205
+ // result.push(copy({
206
+ // targets: toCopy
207
+ // }));
208
+ // }
209
+ // if (this.options.html || this.options.input.endsWith('.html')) {
210
+ // result.push(this.html);
211
+ // result.push(watcher([path.join(this.root, this.options.html)]))
212
+ // }
213
+ // if (this.options.stats) {
214
+ // result.push(this.visualizer);
215
+ // }
216
+ // if (this.options.minify) {
217
+ // result.push(terser({
218
+ // module: true,
219
+ // ecma: 2020,
220
+ // compress: true,
221
+ // keep_classnames: false,
222
+ // keep_fnames: false,
223
+ // mangle: true,
224
+ // output: {
225
+ // comments: false
226
+ // }
227
+ // }));
228
+ // }
229
+ // if (this.options.devServer && this.options.port) {
230
+ // result.push(this.devServer, this.livereload);
231
+ // }
232
+ // return result;
233
+ // }
234
+ //
235
+ // getExternals() {
236
+ // if (!this.options.external)
237
+ // return [];
238
+ // if (Array.isArray(this.options.external))
239
+ // return this.options.external.map(s => new RegExp(s));
240
+ // return Object.keys(this.options.external).map(s => new RegExp(s));
241
+ // }
242
+
243
+ /**
244
+ * @returns {RollupOptions[]}
245
+ */
246
+ getConfig() {
247
+ if (this.options.external && typeof this.options.external === "string")
248
+ this.options.external = [this.options.external]
249
+ console.log(this.options.name, this.options);
250
+ return this.options.module.split(",").map(format => ({
251
+ entryPoints: [
252
+ { out: this.options.name, in: this.options.input }
253
+ ],
254
+ bundle: true,
255
+ minify: this.options.minify,
256
+ sourcemap: true,
257
+ target: ['chrome88', 'safari14', 'firefox88'],
258
+ outdir: 'dist/bundle',
259
+ metafile: true,
260
+ absWorkingDir: this.root,
261
+ format: ({
262
+ es: 'esm'
263
+ })[format] ?? format,
264
+ outExtension: {
265
+ '.js': {
266
+ es: '.js',
267
+ cjs: '.cjs'
268
+ }[format]
269
+ },
270
+ platform: this.options.browser ? 'browser': 'node',
271
+ tsconfig: 'tsconfig.json',
272
+ external: ["*.woff2", "*.woff", ...this.options.external.map(x => x+"*")],
273
+ define: {
274
+ 'process.env.NODE_ENV': '"production"'
275
+ },
276
+ publicPath: '/',
277
+ plugins: [
278
+ lessLoader(),
279
+ ...(this.options.html ? [htmlPlugin({
280
+ files: [{
281
+ entryPoints: [this.options.input],
282
+ filename: 'index.html',
283
+ scriptLoading: 'module',
284
+ htmlTemplate: readFileSync(this.options.html).toString().replace('</head>', `<script type="importmap">
285
+ ${JSON.stringify({
286
+ imports: Object.fromEntries(this.options.external
287
+ .map(key => key.replace('*', '/'))
288
+ .map(key => [key, `/external/${this.options.alias?.[key] ?? key}`]))
289
+ })}
290
+ </script>`),
291
+ // if (!this.options.minify) {
292
+ // const importMaps = Object.fromEntries(this.options.external
293
+ // .map(key => key.replace('.*', '/'))
294
+ // .map(key => [key, `/external/${this.options.alias?.[key] ?? key}`]));
295
+ // inject = `<script type="importmap" >${JSON.stringify({
296
+ // imports: importMaps
297
+ // })}</script>` + inject;
298
+ // }
299
+ // extraScripts: [
300
+ // `data:text/javascript,new EventSource('/esbuild').addEventListener('change', () => location.reload())`,
301
+ // ],
302
+ define: {
303
+ }
304
+ }]
305
+ })] : []),
306
+ ],
307
+
308
+ // output: this.output,
309
+ // external: (this.options.minify && this.options.browser) ? [] : this.getExternals(),
310
+ // manualChunks: this.options.chunks,
311
+ // onwarn(warning) {
312
+ // switch (warning.code) {
313
+ // case 'CIRCULAR_DEPENDENCY':
314
+ // return;
315
+ // case 'THIS_IS_UNDEFINED':
316
+ // console.log(`${warning.message} at`);
317
+ // console.log(`\t${warning.id}`);
318
+ // break;
319
+ // case 'PLUGIN_WARNING':
320
+ // console.log(`${warning.message} at`);
321
+ // console.log(`\t${warning.id}`);
322
+ // break;
323
+ // default:
324
+ // console.warn(`\t${warning.code}(!) ${warning.message}`)
325
+ // }
326
+ //
327
+ // },
328
+ // plugins: this.plugins,
329
+ // preserveEntrySignatures: true,
330
+ // treeshake: this.options.minify ? "recommended" : "safest",
331
+ // watch: {
332
+ // buildDelay: 300,
333
+ // clearScreen: false,
334
+ // exclude: this.getExternals().concat(path.join(this.root, this.outDir)),
335
+ // }
336
+ }));
337
+ }
338
+ }
@@ -1,19 +1,19 @@
1
- import ts from "typescript/lib/typescript.js";
2
- import {resolve, relative} from 'path';
3
- import fs from "fs";
4
- import { lessToStringTransformer } from "./absolute-plugin.js";
1
+ import ts from "typescript";
2
+ import {resolve, relative} from 'node:path';
3
+ import fs from "node:fs";
4
+ import { tsResolvePlugin } from "./ts-resolve-plugin.js";
5
5
 
6
6
  const rootDir = process.cwd();
7
7
 
8
8
  export function compile(...flags) {
9
9
 
10
10
  const host = ts.createSolutionBuilderWithWatchHost(ts.sys, createProgram);
11
- console.log()
12
- host.getCustomTransformers = () => ({
11
+ host.getCustomTransformers = (pkg) => ({
13
12
  before: [
14
- lessToStringTransformer
13
+ tsResolvePlugin
15
14
  ],
16
15
  afterDeclarations: [
16
+ tsResolvePlugin
17
17
  ]
18
18
  });
19
19
  host.useCaseSensitiveFileNames();
@@ -0,0 +1,240 @@
1
+ import ts from "typescript";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import { ImportPathsResolver } from '@zerollup/ts-helpers'
5
+
6
+ class Visitor {
7
+ /**
8
+ * @type {ImportPathsResolver}
9
+ */
10
+ resolver;
11
+ /**
12
+ * @type {ts.TransformationContext}
13
+ */
14
+ context;
15
+ /**
16
+ * @type {ts.CompilerOptions}
17
+ */
18
+ options;
19
+ /**
20
+ * @type {{
21
+ * copy: RegExp;
22
+ * import: RegExp;
23
+ * }}
24
+ */
25
+ config;
26
+ /**
27
+ *
28
+ * @param context {ts.TransformationContext}
29
+ */
30
+ constructor(context, config) {
31
+ this.context = context;
32
+ this.options = context.getCompilerOptions();
33
+ this.resolver = new ImportPathsResolver({
34
+ paths: this.options.paths,
35
+ baseUrl: this.options.baseUrl,
36
+ exclude: []
37
+ });
38
+ this.config = {
39
+ copy: /\.(less|css|scss|sass|png|jpg|ico)$/,
40
+ import: /\.(txt|sql|svg|html)$/,
41
+ ...config
42
+ }
43
+ }
44
+ findFile(importPath, sourceFile) {
45
+ const sourceFileDir = path.dirname(sourceFile.path);
46
+
47
+ return [
48
+ importPath,
49
+ importPath + "/index.ts",
50
+ importPath + ".ts",
51
+ importPath + ".tsx",
52
+ importPath + "/index.js",
53
+ importPath + ".js",
54
+ importPath + ".jsx",
55
+ importPath.replace(/\.js$/, ".ts")
56
+ ].find(x => fs.existsSync(path.resolve(sourceFileDir, x)))?.replace(/(\.ts)x?$/, '.js');
57
+ }
58
+ resolveFile(importPath, sourceFile){
59
+ const sourceFileDir = path.dirname(sourceFile.path);
60
+ const existed = this.findFile(importPath, sourceFile);
61
+ if (existed) return existed;
62
+ const suggestions = this.resolver.getImportSuggestions(importPath, sourceFileDir) ?? [];
63
+ for (let suggestion of suggestions) {
64
+ const existed = this.findFile(suggestion, sourceFile)
65
+ if (existed) return existed;
66
+ }
67
+ return importPath;
68
+ }
69
+ /**
70
+ * @param importPath {string}
71
+ * @param sourceFile {ts.SourceFile}
72
+ */
73
+ resolve(importPath, sourceFile){
74
+ importPath = this.resolveFile(importPath, sourceFile);
75
+ const sourceFileDir = path.dirname(sourceFile.path);
76
+ const caseSensitiveFileNames = this.context.getEmitHost().useCaseSensitiveFileNames();
77
+ const formatPath = caseSensitiveFileNames ? x => x : x => x.toLowerCase();
78
+ const absSource = path.join(this.options.outDir, path.relative(this.options.baseUrl, sourceFileDir));
79
+ const abs = path.resolve(sourceFileDir, importPath);
80
+ if (this.config.import.test(importPath)) {
81
+ const content = fs.readFileSync(path.resolve(sourceFileDir, importPath), 'utf-8');
82
+ const outFile = path.resolve(absSource, importPath).replaceAll(path.sep, '/') + '.js';
83
+ fs.mkdirSync(path.dirname(outFile), {recursive: true});
84
+ fs.writeFileSync(outFile, 'export default `'+content.replaceAll('`','\\`')+'`', 'utf-8');
85
+ return importPath+".js";
86
+ }
87
+ if (this.config.copy.test(importPath)) {
88
+ // console.log(abs, absSource, path.relative(absSource,abs));
89
+ // const outFile = path.resolve(absSource, importPath).replaceAll(path.sep, '/');
90
+ // fs.cpSync(path.resolve(sourceFileDir, importPath), outFile);
91
+ return path.relative(absSource,abs);
92
+ }
93
+ return importPath;
94
+ }
95
+
96
+ visitSourceFile = sourceFile => ts.visitEachChild(sourceFile, node => this.visit(node,sourceFile), this.context);
97
+
98
+ /**
99
+ * @param node {ts.Node}
100
+ * @param sourceFile {ts.SourceFile}
101
+ */
102
+ visit(node,sourceFile){
103
+ // if (node && node.kind == SyntaxKind.ImportDeclaration) {
104
+ // return visitImportNode(node as ts.ImportDeclaration);
105
+ // }
106
+ if (!node)
107
+ return ts.visitEachChild(node, this.visit, this.context);
108
+ if (ts.isCallExpression(node)) {
109
+ const result = this.visitRequireNode(node, sourceFile);
110
+ if (result)
111
+ return result;
112
+ }
113
+ if (ts.isImportDeclaration(node)) {
114
+ const result = this.visitImportNode(node, sourceFile);
115
+ if (result)
116
+ return result;
117
+ }
118
+ if (ts.isExportDeclaration(node)) {
119
+ const result = this.visitExportNode(node, sourceFile);
120
+ if (result)
121
+ return result;
122
+ }
123
+ return ts.visitEachChild(node, node => this.visit(node, sourceFile), this.context);
124
+ }
125
+
126
+
127
+ /**
128
+ * @param exportNode {ts.ExportDeclaration}
129
+ * @param sourceFile {ts.SourceFile}
130
+ */
131
+ visitExportNode(exportNode, sourceFile) {
132
+ if (exportNode.typeOnly){
133
+ console.log('type olnly')
134
+ return ;
135
+ }
136
+ const file = exportNode.moduleSpecifier?.text ?? exportNode.text;
137
+ if (!file)
138
+ return;
139
+
140
+ const resolved = this.resolve(file, sourceFile);
141
+
142
+ const newNode = this.context.factory.updateExportDeclaration(
143
+ exportNode,
144
+ exportNode.decorators,
145
+ exportNode.modifiers,
146
+ exportNode.exportClause,
147
+ this.context.factory.createStringLiteral(resolved),
148
+ exportNode.typeOnly
149
+ );
150
+ if (newNode.flags !== exportNode.flags) {
151
+ newNode.flags = exportNode.flags
152
+ }
153
+ return newNode;
154
+ }
155
+
156
+ /**
157
+ * @param importNode {ts.ImportDeclaration}
158
+ * @param sourceFile {ts.SourceFile}
159
+ */
160
+ visitImportNode(importNode, sourceFile) {
161
+ const file = importNode.moduleSpecifier?.text;
162
+ if (!file)
163
+ return;
164
+ const resolved = this.resolve(file, sourceFile);
165
+
166
+ const newNode = this.context.factory.updateImportDeclaration(
167
+ importNode,
168
+ importNode.modifiers,
169
+ importNode.importClause,
170
+ this.context.factory.createStringLiteral(resolved),
171
+ importNode.assertClause,
172
+ );
173
+ newNode.flags = importNode.flags;
174
+ return newNode;
175
+ // const caseSensitiveFileNames = this.context.getEmitHost().useCaseSensitiveFileNames();
176
+ // const formatPath = caseSensitiveFileNames ? x => x : x => x.toLowerCase();
177
+ // const sourceFileDir = path.dirname(sourceFile.path);
178
+ // const abs = formatPath(path.resolve(sourceFileDir, formatPath(file)));
179
+ // if (/\.(less|css|scss|sass|svg|png|html)$/.test(file)) {
180
+ // const absSource = formatPath(path.join(this.options.outDir, formatPath(path.relative(this.options.baseUrl, sourceFileDir))));
181
+ // const relFile = path.relative(absSource, abs).replaceAll(path.sep, '/');
182
+ // return this.context.factory.updateImportDeclaration(
183
+ // importNode,
184
+ // importNode.decorators,
185
+ // importNode.modifiers,
186
+ // importNode.importClause,
187
+ // importNode.assertClause,
188
+ // this.context.factory.createStringLiteral(relFile)
189
+ // );
190
+ // }
191
+ // if (/\.(json|tsx?|jsx?)$/.test(file))
192
+ // return;
193
+ // if (fs.existsSync(abs + '.ts') || fs.existsSync(abs + '.tsx')) {
194
+ // return this.context.factory.updateImportDeclaration(
195
+ // importNode,
196
+ // importNode.decorators,
197
+ // importNode.modifiers,
198
+ // importNode.importClause,
199
+ // importNode.assertClause,
200
+ // this.context.factory.createStringLiteral(file + '.js')
201
+ // );
202
+ // }
203
+ // if (fs.existsSync(abs + '/')) {
204
+ // const indexFile = `${file}/index.js`;
205
+ // return this.context.factory.updateImportDeclaration(
206
+ // importNode,
207
+ // importNode.decorators,
208
+ // importNode.modifiers,
209
+ // importNode.importClause,
210
+ // importNode.assertClause,
211
+ // this.context.factory.createStringLiteral(indexFile)
212
+ // );
213
+ // }
214
+ }
215
+
216
+ /**
217
+ * @param importNode {ts.Node}
218
+ * @param sourceFile {ts.SourceFile}
219
+ */
220
+ visitRequireNode(importNode, sourceFile) {
221
+ if (importNode.expression.kind !== ts.SyntaxKind.Identifier ||
222
+ importNode.expression.escapedText !== "require") {
223
+ return;
224
+ }
225
+ const file = importNode.arguments[0].text;
226
+ const resolved = this.resolve(file, sourceFile);
227
+ return this.context.factory.updateCallExpression(
228
+ importNode,
229
+ importNode.expression,
230
+ undefined,
231
+ [this.context.factory.createStringLiteral(resolved)]
232
+ );
233
+ }
234
+ }
235
+
236
+ export const tsResolvePlugin = function (contextOrOptions) {
237
+ if(!contextOrOptions.getCompilerOptions)
238
+ return context => new Visitor(context, contextOrOptions).visitSourceFile;
239
+ return new Visitor(contextOrOptions).visitSourceFile;
240
+ };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "module": "NodeNext",
4
- "moduleResolution": "NodeNext",
3
+ "module": "esnext",
4
+ "moduleResolution": "bundler",
5
5
  "target": "ESNext",
6
6
  "composite": true,
7
7
  "sourceMap": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmmn/tools",
3
- "version": "1.8.6",
3
+ "version": "1.9.1",
4
4
  "description": "Compilation, bundling, code generator, testing.",
5
5
  "main": "dist/rollup.config.js",
6
6
  "type": "module",
@@ -41,47 +41,28 @@
41
41
  "test/*"
42
42
  ],
43
43
  "dependencies": {
44
+ "@craftamap/esbuild-plugin-html": "0.6.1",
44
45
  "@jest/globals": "27.x.x",
45
- "@modular-css/rollup": "28",
46
- "@open-wc/rollup-plugin-html": "^1.2.5",
47
- "@rollup/plugin-alias": "3",
48
- "@rollup/plugin-commonjs": "^21",
49
- "@rollup/plugin-image": "2",
50
- "@rollup/plugin-json": "4",
51
- "@rollup/plugin-node-resolve": "^13",
52
- "@rollup/plugin-replace": "4.x.x",
53
- "@rollup/plugin-typescript": "^8",
54
46
  "@swc/jest": "0.2.17",
55
47
  "@testdeck/jest": "0.2.0",
56
48
  "@types/jest": "27.x.x",
57
49
  "@types/sinon": "10.x.x",
58
- "@web/rollup-plugin-html": "^1.10.1",
50
+ "@zerollup/ts-helpers": "1.7.18",
51
+ "esbuild": "0.19.5",
52
+ "esbuild-plugin-less": "1.3.1",
53
+ "esbuild-register": "3.5.0",
54
+ "esbuild-visualizer": "0.4.1",
59
55
  "fast-glob": "^3.2.11",
60
56
  "file-uri-to-path": "2.x.x",
61
57
  "import-meta-resolve": "2",
62
58
  "jest": "29.x.x",
63
59
  "less": "^4",
64
60
  "live-server": "1.2.2",
65
- "postcss-assets": "6",
66
- "postcss-import": "14",
67
- "postcss-modules": "*",
68
- "rollup": "^2",
69
- "rollup-plugin-copy": "3",
70
- "rollup-plugin-livereload": "^2.0.5",
71
- "rollup-plugin-node-builtins": "^2.1.2",
72
- "rollup-plugin-node-globals": "^1.4.0",
73
- "rollup-plugin-serve": "^1.1.0",
74
- "rollup-plugin-sourcemaps": "^0.6.3",
75
- "rollup-plugin-string": "^3.0.0",
76
- "rollup-plugin-styles": "^4",
77
- "rollup-plugin-terser": "^7",
78
- "rollup-plugin-visualizer": "^5.5.4",
79
61
  "sinon": "10.x.x",
80
62
  "ts-jest": "29.x.x",
81
- "typescript-transform-paths": "3.4.6",
82
63
  "unique-slug": "*"
83
64
  },
84
65
  "author": "",
85
66
  "license": "ISC",
86
- "gitHead": "c49859634cc0fb40bc888d468d46f8b47b7ff840"
67
+ "gitHead": "56f8e60e50629a425c3abfb1a67e6cc92c90bbf5"
87
68
  }
package/bundle/images.js DELETED
@@ -1,43 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
- import { basename, dirname, join } from 'path';
3
- import { createFilter } from 'rollup-pluginutils';
4
- const defaultExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp'];
5
-
6
- export function images(options = {}) {
7
- const extensions = options.extensions || defaultExtensions;
8
- const includes = extensions.map(e => `**/*${e}`);
9
- const filter = createFilter(options.include || includes, options.exclude);
10
- const assetsDir = options.output ?? 'assets';
11
- let images = [];
12
-
13
- function generateBundle(outputOptions, rendered) {
14
- if (!images.length)
15
- return;
16
- const outDir =
17
- outputOptions.dir || dirname(outputOptions.dest || outputOptions.file);
18
- const distDir = join(outDir, assetsDir);
19
- if (!existsSync(distDir)) {
20
- mkdirSync(distDir, {recursive: true});
21
- }
22
- images.forEach(id => {
23
- writeFileSync(`${distDir}/${basename(id)}`, readFileSync(id));
24
- });
25
- }
26
-
27
- return {
28
- name: 'image-file',
29
- load(id) {
30
- if ('string' !== typeof id || !filter(id)) {
31
- return null;
32
- }
33
-
34
- if (images.indexOf(id) < 0) {
35
- images.push(id);
36
- }
37
- return `export default '/${assetsDir}/${basename(id)}';`;
38
- },
39
- generateBundle,
40
- ongenerate: generateBundle
41
- };
42
- }
43
-
@@ -1,304 +0,0 @@
1
- import commonjs from '@rollup/plugin-commonjs';
2
- import nodeResolve from '@rollup/plugin-node-resolve';
3
- import {terser} from "rollup-plugin-terser"
4
- import {visualizer} from 'rollup-plugin-visualizer';
5
- import {string} from "rollup-plugin-string";
6
- import serve from 'rollup-plugin-serve';
7
- import builtins from 'rollup-plugin-node-builtins';
8
- import livereload from 'rollup-plugin-livereload';
9
- import copy from 'rollup-plugin-copy';
10
- import fs from "fs";
11
- import path, {join} from "path";
12
- import html from '@open-wc/rollup-plugin-html';
13
- import json from '@rollup/plugin-json';
14
- import alias from '@rollup/plugin-alias';
15
- import replace from '@rollup/plugin-replace';
16
- import sourcemaps from 'rollup-plugin-sourcemaps';
17
- import {Styles} from "./styles.js";
18
- /**
19
- * @typedef {import(rollup).RollupOptions} RollupOptions
20
- * @typedef {import(rollup).OutputOptions} OutputOptions
21
- */
22
-
23
- export class ConfigCreator {
24
-
25
- /**
26
- * @type {{
27
- * minify: boolean,
28
- * input: string,
29
- * devServer: boolean,
30
- * module: string,
31
- * external: string[],
32
- * stats: boolean,
33
- * name: string,
34
- * styles: 'modules' | null,
35
- * outDir: string,
36
- * html: string,
37
- * browser: boolean,
38
- * dedupe: string[],
39
- * target: string
40
- * inject: string
41
- * }}
42
- */
43
- options;
44
-
45
- /**
46
- * @type {string}
47
- */
48
- root = process.cwd();
49
-
50
-
51
- constructor(options) {
52
- this.options = {
53
- module: 'es',
54
- external: [],
55
- name: 'index',
56
- outDir: 'dist/bundle',
57
- ...options
58
- };
59
- if (options.rootDir)
60
- this.root = options.rootDir;
61
- }
62
-
63
- get outDir() {
64
- return path.join(this.root, this.options.outDir);
65
- }
66
-
67
- getOutputFileName(module, minify) {
68
- switch (module) {
69
- case "cjs":
70
- return `[name]${minify ? '.min' : ''}.cjs`;
71
- case "es":
72
- return `[name]${minify ? '.min' : ''}.js`;
73
- default:
74
- return `[name]-${module}${minify ? '.min' : ''}.js`;
75
- }
76
- }
77
-
78
- /**
79
- *
80
- * @returns {OutputOptions}
81
- */
82
- get output() {
83
- // const output = `${this.options.name ?? 'index'}-${this.options.module}${this.options.minify ? '.min' : ''}.js`;
84
- return this.options.module.split(',').map(module => ({
85
- entryFileNames: this.getOutputFileName(module, this.options.minify),
86
- // file: output,
87
- dir: this.outDir,
88
- sourcemap: this.options.minify ? true : 'inline',
89
- format: module,
90
- globals: module === 'umd' ? (Array.isArray(this.options.external) ? Object.fromEntries(this.options.external.map(x => [x, x])) : this.options.external) : [],
91
- assetFileNames: "assets/[name][extname]",
92
- chunkFileNames: "chunks/[name].js",
93
- name: this.options.global ?? 'global',
94
- sourcemapPathTransform: sourceMap => {
95
- const p = path.relative(this.root, path.resolve(this.outDir, sourceMap));
96
- return path.join('/', this.options.package, p);
97
- }
98
- }));
99
- }
100
-
101
- get html() {
102
- return html({
103
- publicPath: '/',
104
- dir: this.outDir,
105
- inject: false,
106
- template: (x) => {
107
- let inject = (this.options.inject === "json") ? `<script>
108
- globalThis.assets = ${JSON.stringify(Object.keys(x.bundle.bundle))};
109
- </script>`: Object.keys(x.bundle.bundle).map(key => {
110
- if (key.endsWith('css'))
111
- return `<link rel="stylesheet" href="${this.options.base ?? ''}/${key}" >`;
112
- if (key.endsWith('js'))
113
- return `<script type="module" defer src="${this.options.base ?? ''}/${key}"></script>`;
114
- }).join('\n');
115
- if (!this.options.minify) {
116
- const importMaps = Object.fromEntries(this.options.external
117
- .map(key => key.replace('.*', '/'))
118
- .map(key => [key, `/external/${this.options.alias?.[key] ?? key}`]));
119
- inject = `<script type="importmap" >${JSON.stringify({
120
- imports: importMaps
121
- })}</script>` + inject;
122
- }
123
- if (this.options.base){
124
- inject = `<base href="${this.options.base}">` + inject;
125
- }
126
- const html = fs.readFileSync(path.join(this.root, this.options.html), 'utf8')
127
- const interpolated = new Function(...Object.keys(this.options), `return \`${html}\`;`)(...Object.values(this.options));
128
- return interpolated.replace('</head>', inject + '</head>');
129
- }
130
- });
131
- }
132
-
133
- get devServer() {
134
- return serve({
135
- open: false,
136
- contentBase: [this.outDir, path.join(this.root, 'assets')],
137
- port: this.options.port,
138
- historyApiFallback: true
139
- });
140
- }
141
-
142
- get livereload() {
143
- return livereload({
144
- watch: [this.outDir, path.join(this.root, 'assets'), this.options.html],
145
- verbose: false, // Disable console output
146
- // other livereload options
147
- port: 12345,
148
- delay: 300,
149
- })
150
- }
151
-
152
- get visualizer() {
153
- return visualizer({
154
- open: true,
155
- sourcemap: true,
156
- template: 'treemap',
157
- brotliSize: true,
158
-
159
- filename: path.join(this.outDir, '/stats.html')
160
- })
161
- }
162
-
163
- get plugins() {
164
- const result = [
165
- replace({
166
- 'process.env.NODE_ENV': JSON.stringify(this.options.minify ? 'production' : 'development'),
167
- preventAssignment: true
168
- }),
169
- ...Styles(this),
170
- commonjs({
171
- requireReturnsDefault: "namespace",
172
- transformMixedEsModules: true,
173
- defaultIsModuleExports: true,
174
- }),
175
- nodeResolve({
176
- browser: this.options.browser,
177
- dedupe: this.options.dedupe || [],
178
- preferBuiltins: !this.options.browser
179
- }),
180
- sourcemaps(),
181
- builtins(),
182
- /*this.options.styles === 'modules' ? postCSS({
183
- mode: [
184
- "inject",
185
- {container: "head", prepend: true, attributes: {id: "global"}},
186
- ],
187
- plugins: [
188
- flexbugs,
189
- ],
190
- modules: {
191
- root: ''
192
- },
193
- namedExports: false,
194
- autoModules: true,
195
- }) : */
196
- Styles(this),
197
-
198
- string({
199
- include: /\.(html|svg|less)$/,
200
- exclude: /\.module\.css/
201
- }),
202
- json(),
203
-
204
- ];
205
- if (this.options.alias) {
206
- result.unshift(alias({
207
- entries: Object.entries(this.options.alias).map(([key, value]) => ({
208
- find: key,
209
- replacement: value.replace('<root>', this.root)
210
- }))
211
- }));
212
- }
213
- if (this.options.minify && this.options.mount){
214
- const toCopy = Object.entries(this.options.mount).map(([to, from]) => {
215
- return {src: from + '/*', dest: join(this.outDir, to)}
216
- });
217
- result.push(copy({
218
- targets: toCopy
219
- }));
220
- }
221
- if (this.options.html || this.options.input.endsWith('.html')) {
222
- result.push(this.html);
223
- result.push(watcher([path.join(this.root, this.options.html)]))
224
- }
225
- if (this.options.stats) {
226
- result.push(this.visualizer);
227
- }
228
- if (this.options.minify) {
229
- result.push(terser({
230
- module: true,
231
- ecma: 2020,
232
- compress: true,
233
- keep_classnames: false,
234
- keep_fnames: false,
235
- mangle: true,
236
- output: {
237
- comments: false
238
- }
239
- }));
240
- }
241
- if (this.options.devServer && this.options.port) {
242
- result.push(this.devServer, this.livereload);
243
- }
244
- return result;
245
- }
246
-
247
- getExternals() {
248
- if (!this.options.external)
249
- return [];
250
- if (Array.isArray(this.options.external))
251
- return this.options.external.map(s => new RegExp(s));
252
- return Object.keys(this.options.external).map(s => new RegExp(s));
253
- }
254
-
255
- /**
256
- * @returns {RollupOptions[]}
257
- */
258
- getConfig() {
259
- if (this.options.external && typeof this.options.external === "string")
260
- this.options.external = [this.options.external]
261
- console.log(this.options.name, this.options);
262
- return [{
263
- input: {
264
- [this.options.name]: path.join(this.root, this.options.input)
265
- },
266
- output: this.output,
267
- external: (this.options.minify && this.options.browser) ? [] : this.getExternals(),
268
- manualChunks: this.options.chunks,
269
- onwarn(warning) {
270
- switch (warning.code) {
271
- case 'CIRCULAR_DEPENDENCY':
272
- return;
273
- case 'THIS_IS_UNDEFINED':
274
- console.log(`${warning.message} at`);
275
- console.log(`\t${warning.id}`);
276
- break;
277
- case 'PLUGIN_WARNING':
278
- console.log(`${warning.message} at`);
279
- console.log(`\t${warning.id}`);
280
- break;
281
- default:
282
- console.warn(`\t${warning.code}(!) ${warning.message}`)
283
- }
284
-
285
- },
286
- plugins: this.plugins,
287
- preserveEntrySignatures: true,
288
- treeshake: this.options.minify ? "recommended" : "safest",
289
- watch: {
290
- buildDelay: 300,
291
- clearScreen: false,
292
- exclude: this.getExternals().concat(path.join(this.root, this.outDir)),
293
- }
294
- }]
295
- }
296
- }
297
-
298
- const watcher = (files) => ({
299
- buildStart() {
300
- for (const file of files) {
301
- this.addWatchFile(file);
302
- }
303
- },
304
- });
package/bundle/styles.js DELETED
@@ -1,56 +0,0 @@
1
- import postCssImport from 'postcss-import';
2
- import postCssAssets from 'postcss-assets';
3
- import cssModules from '@modular-css/rollup';
4
- import slug from "unique-slug";
5
- import styles from "rollup-plugin-styles";
6
- import {images} from "./images.js";
7
-
8
- /**
9
- *
10
- * @param {ConfigCreator} config
11
- * @returns {(*)[]}
12
- * @constructor
13
- */
14
- export function Styles(config) {
15
- return [
16
- images({
17
- output: 'assets'
18
- // exclude: 'node_modules/**'
19
- }),
20
- config.options.styles === "modules" ? cssModules({
21
- common: 'common.css',
22
- before: [postCssImport, postCssAssets],
23
- namer: function (file, selector) {
24
- return selector + "_" +
25
- file.replace(/([\/\\]index)?(\.module)?\.css$/, "").split(/[\\\/]/).pop() + "_" +
26
- slug(file)
27
- }
28
- }): styles({
29
- mode: "emit"
30
- }),
31
- // postcss({
32
- // extract: 'output.css',
33
- // ident: 'postcss',
34
- // modules: {
35
- // root: '',
36
- // generateScopedName: `[name]_[local]_[hash:base64:5]`,
37
- // Loader: Loader
38
- // },
39
- // use: ['sass'],
40
- // plugins: [
41
- // // postCssDuplicates(),
42
- // postCssImport()
43
- // // postcssModules({
44
- // // generateScopedName: '[local]',
45
- // // root: '',
46
- // // Loader: Loader
47
- // // }),
48
- // // postcssPresetEnv({
49
- // // stage: 0,
50
- // // }),
51
- // ],
52
- // }),
53
- ];
54
- }
55
-
56
-
@@ -1,98 +0,0 @@
1
- import ts from "typescript";
2
- import path from "path";
3
- import fs from "fs";
4
-
5
- function visitExportNode(exportNode, sourceFile) {
6
- if (exportNode.typeOnly){
7
- console.log('type olnly')
8
- return ;
9
- }
10
- const file = exportNode.moduleSpecifier?.text ?? exportNode.test;
11
- if (!file || !file.startsWith('.'))
12
- return;
13
- const sourceFileDir = path.dirname(sourceFile.path);
14
- const abs = path.resolve(sourceFileDir, file);
15
- if (/\.(less|css|scss|sass|svg|png|html)$/.test(file)) {
16
- const absSource = path.join(options.outDir, path.relative(options.baseUrl, sourceFileDir));
17
- const relFile = path.relative(absSource, abs).replaceAll(path.sep, '/');
18
- return ts.updateExportDeclaration(exportNode, exportNode.decorators, exportNode.modifiers, exportNode.exportClause, ts.createStringLiteral(relFile), exportNode.typeOnly);
19
- }
20
- if (fs.existsSync(abs + '.ts') || fs.existsSync(abs + '.tsx')) {
21
- return ts.updateExportDeclaration(exportNode, exportNode.decorators, exportNode.modifiers, exportNode.exportClause, ts.createStringLiteral(file + '.js'), exportNode.typeOnly);
22
- }
23
- if (fs.existsSync(abs + '/')) {
24
- const indexFile = `${file}/index.js`;
25
- return ts.updateExportDeclaration(exportNode, exportNode.decorators, exportNode.modifiers, exportNode.exportClause, ts.createStringLiteral(indexFile), exportNode.typeOnly);
26
- }
27
- }
28
-
29
- function visitImportNode(importNode, sourceFile, options, context) {
30
- const factory = context.factory;
31
- const file = importNode.moduleSpecifier?.text;
32
- if (!file || !file.startsWith('.'))
33
- return;
34
- const caseSensitiveFileNames = context.getEmitHost().useCaseSensitiveFileNames();
35
- const formatPath = caseSensitiveFileNames ? x => x : x => x.toLowerCase();
36
- const sourceFileDir = path.dirname(sourceFile.path);
37
- const abs = formatPath(path.resolve(sourceFileDir, formatPath(file)));
38
- if (/\.(less|css|scss|sass|svg|png|html)$/.test(file)) {
39
- const absSource = formatPath(path.join(options.outDir, formatPath(path.relative(options.baseUrl, sourceFileDir))));
40
- const relFile = path.relative(absSource, abs).replaceAll(path.sep, '/');
41
- return factory.updateImportDeclaration(importNode, importNode.decorators, importNode.modifiers, importNode.importClause, factory.createStringLiteral(relFile));
42
- }
43
- if (/\.(json|tsx?|jsx?)$/.test(file))
44
- return;
45
- if (fs.existsSync(abs + '.ts') || fs.existsSync(abs + '.tsx')) {
46
- return factory.updateImportDeclaration(importNode, importNode.decorators, importNode.modifiers, importNode.importClause, factory.createStringLiteral(file + '.js'));
47
- }
48
- if (fs.existsSync(abs + '/')) {
49
- const indexFile = `${file}/index.js`;
50
- return factory.updateImportDeclaration(importNode, importNode.decorators, importNode.modifiers, importNode.importClause, factory.createStringLiteral(indexFile));
51
- }
52
- }
53
-
54
- function visitRequireNode(importNode, sourceFile) {
55
- if (!(importNode.expression.kind == ts.SyntaxKind.Identifier &&
56
- importNode.expression.escapedText == "require")) {
57
- return;
58
- }
59
- const file = importNode.arguments[0].text;
60
- if (/\.(less|css|scss|sass|svg|png|html)/.test(file)) {
61
- const sourceFileDir = path.dirname(sourceFile.path);
62
- const abs = path.join(sourceFileDir, file);
63
- const absSource = path.join(options.outDir, path.relative(options.baseUrl, sourceFileDir));
64
- const relFile = path.relative(absSource, abs).replaceAll(path.sep, '/');
65
- return ts.updateCall(importNode, importNode.expression, undefined, [ts.createStringLiteral(relFile)]);
66
- }
67
- }
68
-
69
- export const lessToStringTransformer = function (context) {
70
- const options = context.getCompilerOptions();
71
- return (sourceFile) => {
72
- function visitor(node) {
73
- // if (node && node.kind == ts.SyntaxKind.ImportDeclaration) {
74
- // return visitImportNode(node as ts.ImportDeclaration);
75
- // }
76
- if (!node)
77
- return ts.visitEachChild(node, visitor, context);
78
- if (ts.isCallExpression(node)) {
79
- const result = visitRequireNode(node, sourceFile);
80
- if (result)
81
- return result;
82
- }
83
- if (ts.isImportDeclaration(node)) {
84
- const result = visitImportNode(node, sourceFile, options, context);
85
- if (result)
86
- return result;
87
- }
88
- if (ts.isExportDeclaration(node)) {
89
- const result = visitExportNode(node, sourceFile);
90
- if (result)
91
- return result;
92
- }
93
- return ts.visitEachChild(node, visitor, context);
94
- }
95
-
96
- return ts.visitEachChild(sourceFile, visitor, context);
97
- };
98
- };