neo.mjs 9.5.0 → 9.6.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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='9.5.0'
23
+ * @member {String} version='9.6.0'
24
24
  */
25
- version: '9.5.0'
25
+ version: '9.6.0'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2025-05-25",
19
+ "datePublished": "2025-06-01",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -107,7 +107,7 @@ class FooterContainer extends Container {
107
107
  }, {
108
108
  module: Component,
109
109
  cls : ['neo-version'],
110
- html : 'v9.5.0'
110
+ html : 'v9.6.0'
111
111
  }]
112
112
  }],
113
113
  /**
@@ -23,7 +23,7 @@ program
23
23
  .name(programName)
24
24
  .version(packageJson.version)
25
25
  .option('-i, --info', 'print environment debug info')
26
- .option('-e, --env <value>', '"all", "dev", "prod"')
26
+ .option('-e, --env <value>', '"all", "dev", "esm", "prod"')
27
27
  .option('-l, --npminstall <value>', '"yes", "no"')
28
28
  .option('-f, --framework')
29
29
  .option('-n, --noquestions')
@@ -74,7 +74,7 @@ if (programOpts.info) {
74
74
  type : 'list',
75
75
  name : 'env',
76
76
  message: 'Please choose the environment:',
77
- choices: ['all', 'dev', 'prod'],
77
+ choices: ['all', 'dev', 'esm', 'prod'],
78
78
  default: 'all'
79
79
  });
80
80
  }
@@ -129,17 +129,24 @@ if (programOpts.info) {
129
129
  childProcess.status && process.exit(childProcess.status);
130
130
  }
131
131
 
132
- if (themes === 'yes') {
132
+ if (themes === 'yes') {
133
133
  childProcess = spawnSync('node', [`${neoPath}/buildScripts/buildThemes.mjs`].concat(cpArgs), cpOpts);
134
134
  childProcess.status && process.exit(childProcess.status);
135
135
  }
136
136
 
137
- if (threads === 'yes') {
138
- childProcess = spawnSync('node', [`${webpackPath}/buildThreads.mjs`].concat(cpArgs), cpOpts);
139
- childProcess.status && process.exit(childProcess.status);
137
+ if (threads === 'yes') {
138
+ if (env !== 'esm') {
139
+ childProcess = spawnSync('node', [`${webpackPath}/buildThreads.mjs`].concat(cpArgs), cpOpts);
140
+ childProcess.status && process.exit(childProcess.status);
141
+ }
142
+
143
+ if (env === 'all' || env === 'esm') {
144
+ childProcess = spawnSync('node', [`${neoPath}/buildScripts/buildESModules.mjs`], cpOpts);
145
+ childProcess.status && process.exit(childProcess.status);
146
+ }
140
147
  }
141
148
 
142
- if (parsedocs === 'yes') {
149
+ if (parsedocs === 'yes') {
143
150
  childProcess = spawnSync(npmCmd, ['run', 'generate-docs-json'], cpOpts);
144
151
  childProcess.status && process.exit(childProcess.status);
145
152
  }
@@ -0,0 +1,112 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import {minify as minifyJs} from 'terser';
4
+ import {minifyHtml} from './util/minifyHtml.mjs';
5
+ import {fileURLToPath} from 'url';
6
+
7
+ const
8
+ __filename = fileURLToPath(import.meta.url),
9
+ __dirname = path.dirname(__filename),
10
+ inputDirectories = ['apps', 'docs', 'examples', 'src'],
11
+ outputBasePath = '../dist/esm/';
12
+
13
+ async function minifyDirectory(inputDir, outputDir) {
14
+ if (fs.existsSync(inputDir)) {
15
+ fs.mkdirSync(outputDir, {recursive: true});
16
+
17
+ const dirents = fs.readdirSync(inputDir, {recursive: true, withFileTypes: true});
18
+
19
+ for (const dirent of dirents) {
20
+ if (dirent.isFile()) {
21
+ const
22
+ inputPath = path.join(dirent.path, dirent.name),
23
+ relativePath = path.relative(inputDir, inputPath),
24
+ outputPath = path.join(outputDir, relativePath),
25
+ content = fs.readFileSync(inputPath, 'utf8');
26
+
27
+ fs.mkdirSync(path.dirname(outputPath), {recursive: true});
28
+
29
+ try {
30
+ // Minify JSON files
31
+ if (dirent.name.endsWith('.json')) {
32
+ const jsonContent = JSON.parse(content);
33
+
34
+ if (dirent.name === 'neo-config.json') {
35
+ jsonContent.environment = 'dist/esm';
36
+ }
37
+
38
+ fs.writeFileSync(outputPath, JSON.stringify(jsonContent));
39
+ console.log(`Minified JSON: ${inputPath} -> ${outputPath}`);
40
+ }
41
+ // Minify HTML files
42
+ else if (dirent.name.endsWith('.html')) {
43
+ const minifiedContent = await minifyHtml(content);
44
+
45
+ fs.writeFileSync(outputPath, minifiedContent);
46
+ console.log(`Minified HTML: ${inputPath} -> ${outputPath}`);
47
+ }
48
+ // Minify JS files
49
+ else if (dirent.name.endsWith('.mjs')) {
50
+ const result = await minifyJs(content, {
51
+ module: true,
52
+ compress: {
53
+ dead_code: true
54
+ },
55
+ mangle: {
56
+ toplevel: true
57
+ }
58
+ });
59
+
60
+ fs.writeFileSync(outputPath, result.code);
61
+ console.log(`Minified JS: ${inputPath} -> ${outputPath}`);
62
+ }
63
+ } catch (e) {
64
+ console.error(`Error minifying ${inputPath}:`, e);
65
+ }
66
+ }
67
+ // Copy resources folders
68
+ else if (dirent.name === 'resources') {
69
+ const
70
+ inputPath = path.join(dirent.path, dirent.name),
71
+ relativePath = path.relative(inputDir, inputPath),
72
+ outputPath = path.join(outputDir, relativePath);
73
+
74
+ fs.mkdirSync(path.dirname(outputPath), {recursive: true});
75
+
76
+ fs.copySync(inputPath, outputPath);
77
+
78
+ // Minify all JSON files inside the copied folder
79
+ const resourcesEntries = fs.readdirSync(outputPath, {recursive: true, withFileTypes: true});
80
+
81
+ for (const resource of resourcesEntries) {
82
+ if (resource.isFile()) {
83
+ if (resource.name.endsWith('.json')) {
84
+ const
85
+ resourcePath = path.join(resource.path, resource.name),
86
+ content = fs.readFileSync(resourcePath, 'utf8');
87
+
88
+ fs.writeFileSync(resourcePath, JSON.stringify(JSON.parse(content)));
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ const promises = [];
98
+
99
+ // Execute the minification
100
+ inputDirectories.forEach(folder => {
101
+ promises.push(minifyDirectory(path.resolve(__dirname, '../' + folder), path.resolve(__dirname, outputBasePath, folder))
102
+ .catch(err => {
103
+ console.error('Minification failed:', err);
104
+ process.exit(1) // Exit with error code
105
+ })
106
+ );
107
+ });
108
+
109
+ Promise.all(promises).then(() => {
110
+ console.log('Minification complete.');
111
+ process.exit()
112
+ });
@@ -34,7 +34,7 @@ program
34
34
  .name(programName)
35
35
  .version(packageJson.version)
36
36
  .option('-i, --info', 'print environment debug info')
37
- .option('-e, --env <value>', '"all", "dev", "prod"')
37
+ .option('-e, --env <value>', '"all", "dev", "esm", "prod"')
38
38
  .option('-f, --framework')
39
39
  .option('-n, --noquestions')
40
40
  .option('-t, --themes <value>', ["all", ...themeFolders].join(", "))
@@ -83,19 +83,19 @@ if (programOpts.info) {
83
83
  type : 'list',
84
84
  name : 'env',
85
85
  message: 'Please choose the environment:',
86
- choices: ['all', 'dev', 'prod'],
86
+ choices: ['all', 'dev', 'esm', 'prod'],
87
87
  default: 'all'
88
88
  });
89
89
  }
90
90
  }
91
91
 
92
- inquirer.prompt(questions).then(answers => {
92
+ inquirer.prompt(questions).then(async answers => {
93
93
  const env = answers.env || programOpts.env || 'all',
94
94
  themes = answers.themes || programOpts.themes || 'all',
95
95
  insideNeo = programOpts.framework || false,
96
96
  startDate = new Date(),
97
- fileCount = {development: 0, production: 0},
98
- totalFiles = {development: 0, production: 0};
97
+ fileCount = {development: 0, esm: 0, production: 0},
98
+ totalFiles = {development: 0, esm: 0, production: 0};
99
99
 
100
100
  let themeMap;
101
101
 
@@ -124,14 +124,14 @@ if (programOpts.info) {
124
124
  * @param {String} p
125
125
  * @param {String} mode development or production
126
126
  */
127
- function buildEnv(p, mode) {
128
- parseScssFiles(getAllScssFiles(path.join(p, 'src')), mode, 'src');
127
+ async function buildEnv(p, mode) {
128
+ await parseScssFiles(getAllScssFiles(path.join(p, 'src')), mode, 'src');
129
129
 
130
- themeFolders.forEach(themeFolder => {
130
+ for (const themeFolder of themeFolders) {
131
131
  if (themes === 'all' || themes === themeFolder) {
132
- parseScssFiles(getAllScssFiles(path.join(p, themeFolder)), mode, themeFolder);
132
+ await parseScssFiles(getAllScssFiles(path.join(p, themeFolder)), mode, themeFolder);
133
133
  }
134
- });
134
+ }
135
135
  }
136
136
 
137
137
  /**
@@ -247,17 +247,17 @@ if (programOpts.info) {
247
247
  * @param {String} mode development or production
248
248
  * @param {String} target src or a theme
249
249
  */
250
- function parseScssFiles(files, mode, target) {
251
- let devMode = mode === 'development',
252
- map;
250
+ async function parseScssFiles(files, mode, target) {
251
+ let devMode = mode === 'development';
253
252
 
254
253
  totalFiles[mode] += files.length;
255
254
 
256
- files.forEach(file => {
255
+ for (const file of files) {
257
256
  addItemToThemeMap(file, target);
258
257
 
259
258
  let folderPath = path.resolve(cwd, `dist/${mode}/css/${target}/${file.relativePath}`),
260
- destPath = path.resolve(folderPath, `${file.name}.css`);
259
+ destPath = path.resolve(folderPath, `${file.name}.css`),
260
+ map;
261
261
 
262
262
  let result = sass.compile(file.path, {
263
263
  outFile : destPath,
@@ -267,71 +267,71 @@ if (programOpts.info) {
267
267
 
268
268
  const plugins = [autoprefixer];
269
269
 
270
- if (mode === 'production') {
271
- plugins.push(cssnano);
270
+ if (mode === 'esm' || mode === 'production') {
271
+ plugins.push(cssnano) // CSSNano works async only
272
272
  }
273
273
 
274
274
  map = result.sourceMap;
275
275
 
276
- postcss(plugins).process(result.css, {
276
+ const postcssResult = await postcss(plugins).process(result.css, {
277
277
  from: file.path,
278
278
  to : destPath,
279
279
  map : !devMode ? null : {
280
280
  inline: false,
281
281
  prev : map && JSON.stringify(map)
282
282
  }
283
- }).then(postcssResult => {
284
- fs.mkdirpSync(folderPath);
285
- fileCount[mode]++;
283
+ });
286
284
 
287
- let map = postcssResult.map,
288
- processTime = (Math.round((new Date - startDate) * 100) / 100000).toFixed(2);
285
+ fs.mkdirpSync(folderPath);
286
+ fileCount[mode]++;
289
287
 
290
- console.log('Writing file:', (fileCount[mode] + fileCount[mode]), chalk.blue(`${processTime}s`), destPath);
288
+ let currentFileNumber = fileCount.development + fileCount.esm + fileCount.production,
289
+ processTime = (Math.round((new Date - startDate) * 100) / 100000).toFixed(2);
291
290
 
292
- fs.writeFileSync(
293
- destPath,
294
- map ?
295
- `${postcssResult.css}\n\n/*# sourceMappingURL=${path.relative(path.dirname(destPath), postcssResult.opts.to + '.map')} */` :
296
- postcssResult.css,
297
- () => true
298
- );
291
+ map = postcssResult.map;
299
292
 
300
- if (map) {
301
- let mapString = map.toString(),
302
- jsonMap = JSON.parse(mapString),
303
- sources = jsonMap.sources;
304
-
305
- // Somehow files contain both: a relative & an absolute file url
306
- // We only want to keep the relative ones.
307
- [...sources].forEach((item, index) => {
308
- if (!item.startsWith('../')) {
309
- sources.splice(index, 1);
310
- }
311
- });
293
+ console.log('Writing file:', currentFileNumber, chalk.blue(`${processTime}s`), destPath);
312
294
 
313
- map = JSON.stringify(jsonMap);
295
+ fs.writeFileSync(
296
+ destPath,
297
+ map ?
298
+ `${postcssResult.css}\n\n/*# sourceMappingURL=${path.relative(path.dirname(destPath), postcssResult.opts.to + '.map')} */` :
299
+ postcssResult.css,
300
+ () => true
301
+ );
314
302
 
315
- fs.writeFileSync(postcssResult.opts.to + '.map', map);
316
- }
303
+ if (map) {
304
+ let mapString = map.toString(),
305
+ jsonMap = JSON.parse(mapString),
306
+ sources = jsonMap.sources;
317
307
 
318
- if (fileCount[mode] === totalFiles[mode]) {
319
- fs.writeFileSync(
320
- path.resolve(cwd, themeMapFile),
321
- JSON.stringify(themeMap, null, 0)
322
- );
308
+ // Somehow files contain both: a relative & an absolute file url
309
+ // We only want to keep the relative ones.
310
+ [...sources].forEach((item, index) => {
311
+ if (!item.startsWith('../')) {
312
+ sources.splice(index, 1);
313
+ }
314
+ });
323
315
 
324
- fs.mkdirpSync(path.join(cwd, '/dist/', mode, '/resources'), {
325
- recursive: true
326
- });
316
+ map = JSON.stringify(jsonMap);
327
317
 
328
- fs.writeFileSync(
329
- path.join(cwd, '/dist/', mode, themeMapFile),
330
- JSON.stringify(themeMap, null, 0)
331
- );
332
- }
333
- });
334
- });
318
+ fs.writeFileSync(postcssResult.opts.to + '.map', map);
319
+ }
320
+
321
+ if (fileCount[mode] === totalFiles[mode]) {
322
+ fs.writeFileSync(
323
+ path.resolve(cwd, themeMapFile),
324
+ JSON.stringify(themeMap, null, 0)
325
+ );
326
+
327
+ fs.mkdirpSync(path.join(cwd, '/dist/', mode, '/resources'), {recursive: true});
328
+
329
+ fs.writeFileSync(
330
+ path.join(cwd, '/dist/', mode, themeMapFile),
331
+ JSON.stringify(themeMap, null, 0)
332
+ );
333
+ }
334
+ }
335
335
  }
336
336
 
337
337
  themeMap = getThemeMap(themeMapFile);
@@ -339,13 +339,28 @@ if (programOpts.info) {
339
339
  // dist/development
340
340
  if (env === 'all' || env === 'dev') {
341
341
  console.log(chalk.blue(`${programName} starting dist/development`));
342
- buildEnv(scssPath, 'development');
342
+ await buildEnv(scssPath, 'development');
343
+ }
344
+
345
+ // dist/esm
346
+ if (env === 'all' || env === 'esm') {
347
+ console.log(chalk.blue(`${programName} starting dist/esm`));
348
+ await buildEnv(scssPath, 'esm');
343
349
  }
344
350
 
345
351
  // dist/production
346
- if (env === 'all' || env === 'prod') {
352
+ if (env === 'prod') {
347
353
  console.log(chalk.blue(`${programName} starting dist/production`));
348
- buildEnv(scssPath, 'production');
354
+ await buildEnv(scssPath, 'production');
355
+ } else if (env === 'all') {
356
+ // dist/esm & dist/production contain the same output, so we can just copy it over.
357
+ const cssPath = path.join(cwd, '/dist/production/css');
358
+
359
+ fs.mkdirpSync(cssPath, {recursive: true});
360
+ fs.mkdirpSync(path.join(cwd, '/dist/production/resources'), {recursive: true});
361
+
362
+ fs.copySync(path.join(cwd, '/dist/esm/css'), cssPath);
363
+ fs.copySync(path.join(cwd, '/dist/esm/resources/theme-map.json'), path.join(cwd, '/dist/production/resources/theme-map.json'));
349
364
  }
350
365
  });
351
366
  }
@@ -18,5 +18,5 @@ if (!programOpts.target) {
18
18
  throw new Error('Missing -t param');
19
19
  }
20
20
 
21
- fs.mkdirpSync(programOpts.target);
21
+ fs.mkdirpSync(programOpts.target, {recursive: true});
22
22
  fs.copySync(programOpts.source, programOpts.target);
@@ -0,0 +1,26 @@
1
+ import {minify} from 'html-minifier-terser';
2
+
3
+ const
4
+ regexBlankAfterColon = /: /g,
5
+ regexBlankAfterComma = /, /g,
6
+ regexIndexNodeModules = /node_modules/g;
7
+
8
+ export async function minifyHtml(content) {
9
+ const minifiedContent = await minify(content, {
10
+ collapseWhitespace : true,
11
+ minifyCSS : true,
12
+ minifyJS : true,
13
+ processScripts : ['application/ld+json'],
14
+ removeComments : true,
15
+ removeEmptyAttributes : true,
16
+ removeRedundantAttributes : true,
17
+ removeScriptTypeAttributes : true,
18
+ removeStyleLinkTypeAttributes: true,
19
+ useShortDoctype : true
20
+ });
21
+
22
+ return minifiedContent
23
+ .replace(regexBlankAfterColon, ':')
24
+ .replace(regexBlankAfterComma, ',')
25
+ .replace(regexIndexNodeModules, '../../node_modules')
26
+ }
@@ -1,21 +1,19 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import {spawnSync} from 'child_process';
4
- import webpack from 'webpack';
5
-
6
- const cwd = process.cwd(),
7
- cpOpts = {env: process.env, cwd: cwd, stdio: 'inherit', shell: true},
8
- requireJson = path => JSON.parse(fs.readFileSync((path))),
9
- packageJson = requireJson(path.resolve(cwd, 'package.json')),
10
- neoPath = packageJson.name.includes('neo.mjs') ? './' : './node_modules/neo.mjs/',
11
- buildTarget = requireJson(path.resolve(neoPath, 'buildScripts/webpack/production/buildTarget.json')),
12
- filenameConfig = requireJson(path.resolve(neoPath, 'buildScripts/webpack/json/build.json')),
13
- plugins = [],
14
- regexIndexNodeModules = /node_modules/g,
15
- regexLineBreak = /(\r\n|\n|\r)/gm,
16
- regexTopLevel = /\.\.\//g,
17
- regexTrimEnd = /\s+$/gm,
18
- regexTrimStart = /^\s+/gm;
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import {spawnSync} from 'child_process';
4
+ import {minifyHtml} from '../../util/minifyHtml.mjs';
5
+ import webpack from 'webpack';
6
+
7
+ const
8
+ cwd = process.cwd(),
9
+ cpOpts = {env: process.env, cwd: cwd, stdio: 'inherit', shell: true},
10
+ requireJson = path => JSON.parse(fs.readFileSync((path))),
11
+ packageJson = requireJson(path.resolve(cwd, 'package.json')),
12
+ neoPath = packageJson.name.includes('neo.mjs') ? './' : './node_modules/neo.mjs/',
13
+ buildTarget = requireJson(path.resolve(neoPath, 'buildScripts/webpack/production/buildTarget.json')),
14
+ filenameConfig = requireJson(path.resolve(neoPath, 'buildScripts/webpack/json/build.json')),
15
+ plugins = [],
16
+ regexTopLevel = /\.\.\//g;
19
17
 
20
18
  let contextAdjusted = false,
21
19
  examplesPath;
@@ -24,7 +22,7 @@ if (!buildTarget.folder) {
24
22
  buildTarget.folder = 'dist/production';
25
23
  }
26
24
 
27
- export default env => {
25
+ export default async function(env) {
28
26
  let apps = [],
29
27
  examples = [],
30
28
  insideNeo = env.insideNeo == 'true',
@@ -59,7 +57,7 @@ export default env => {
59
57
  }
60
58
  };
61
59
 
62
- const createStartingPoint = (key, folder) => {
60
+ const createStartingPoint = async (key, folder) => {
63
61
  let basePath = '',
64
62
  workerBasePath = '',
65
63
  treeLevel = key.replace('.', '/').split('/').length + (key === 'Docs' ? 2 : 3),
@@ -105,15 +103,9 @@ export default env => {
105
103
  // index.html
106
104
  inputPath = path.resolve(cwd, folder, lAppName, 'index.html');
107
105
  outputPath = path.resolve(cwd, buildTarget.folder, folder, lAppName, 'index.html');
106
+ content = await minifyHtml(fs.readFileSync(inputPath, 'utf-8'));
108
107
 
109
- content = fs.readFileSync(inputPath).toString()
110
- .replace(regexIndexNodeModules, '../../node_modules')
111
- .replace(regexTrimStart, '')
112
- .replace(regexTrimEnd, '')
113
- .replace(', ', ',')
114
- .replace(regexLineBreak, '');
115
-
116
- fs.writeFileSync(outputPath, content);
108
+ fs.writeFileSync(outputPath, content)
117
109
  };
118
110
 
119
111
  const isFile = fileName => fs.lstatSync(fileName).isFile();
@@ -136,13 +128,13 @@ export default env => {
136
128
 
137
129
  parseFolder(apps, path.join(cwd, 'apps'), 0, '');
138
130
 
139
- apps.forEach(key => {
131
+ for (const key of apps) {
140
132
  copyResources(path.join('apps', key, '/resources'));
141
- createStartingPoint(key.substr(1), 'apps');
142
- });
133
+ await createStartingPoint(key.substr(1), 'apps');
134
+ }
143
135
 
144
136
  if (fs.existsSync(path.join(cwd, 'docs'))) {
145
- createStartingPoint('Docs', '');
137
+ await createStartingPoint('Docs', '');
146
138
  }
147
139
 
148
140
  examplesPath = path.join(cwd, 'examples');
@@ -150,10 +142,10 @@ export default env => {
150
142
  if (fs.existsSync(examplesPath)) {
151
143
  parseFolder(examples, examplesPath, 0, '');
152
144
 
153
- examples.forEach(key => {
145
+ for (const key of examples) {
154
146
  copyResources(path.join('examples', key, '/resources'));
155
- createStartingPoint(key.substr(1), 'examples');
156
- });
147
+ await createStartingPoint(key.substr(1), 'examples');
148
+ }
157
149
  }
158
150
 
159
151
  return {
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='9.5.0'
23
+ * @member {String} version='9.6.0'
24
24
  */
25
- version: '9.5.0'
25
+ version: '9.6.0'
26
26
  }
27
27
 
28
28
  /**
@@ -31,7 +31,7 @@ class Viewport extends BaseViewport {
31
31
  async onLoadGridContainerButtonClick(data) {
32
32
  data.component.disabled = true;
33
33
 
34
- let items = await this.loadItems('../../examples/serverside/gridContainer/resources/data/grid-container.json');
34
+ let items = await this.loadItems({url: '../../examples/serverside/gridContainer/resources/data/grid-container.json'});
35
35
 
36
36
  this.getReference('container').add(items)
37
37
  }
@@ -30,7 +30,7 @@ class Viewport extends BaseViewport {
30
30
  async onLoadItemsButtonClick(data) {
31
31
  data.component.disabled = true;
32
32
 
33
- let items = await this.loadItems('../../examples/serverside/toolbarItems/resources/data/toolbar-items.json');
33
+ let items = await this.loadItems({url: '../../examples/serverside/toolbarItems/resources/data/toolbar-items.json'});
34
34
 
35
35
  this.getReference('toolbar').add(items)
36
36
  }
package/package.json CHANGED
@@ -1,33 +1,34 @@
1
1
  {
2
- "name": "neo.mjs",
3
- "version": "9.5.0",
4
- "description": "The webworkers driven UI framework",
5
- "type": "module",
6
- "repository": {
2
+ "name" : "neo.mjs",
3
+ "version" : "9.6.0",
4
+ "description" : "The webworkers driven UI framework",
5
+ "type" : "module",
6
+ "repository" : {
7
7
  "type": "git",
8
- "url": "https://github.com/neomjs/neo.git"
8
+ "url" : "https://github.com/neomjs/neo.git"
9
9
  },
10
- "bin": {
10
+ "bin" : {
11
11
  "neo-cc": "./buildScripts/createClass.mjs"
12
12
  },
13
- "scripts": {
14
- "add-config": "node ./buildScripts/addConfig.mjs",
15
- "build-all": "node ./buildScripts/buildAll.mjs -f -n",
16
- "build-all-questions": "node ./buildScripts/buildAll.mjs -f",
17
- "build-themes": "node ./buildScripts/buildThemes.mjs -f",
18
- "build-threads": "node ./buildScripts/webpack/buildThreads.mjs -f",
19
- "convert-design-tokens": "node ./buildScripts/convertDesignTokens.mjs",
20
- "create-app": "node ./buildScripts/createApp.mjs",
21
- "create-app-minimal": "node ./buildScripts/createAppMinimal.mjs",
22
- "create-class": "node ./buildScripts/createClass.mjs",
23
- "create-component": "node ./buildScripts/createComponent.mjs",
24
- "generate-docs-json": "node ./buildScripts/docs/jsdocx.mjs",
13
+ "scripts" : {
14
+ "add-config" : "node ./buildScripts/addConfig.mjs",
15
+ "build-all" : "node ./buildScripts/buildAll.mjs -f -n",
16
+ "build-all-questions" : "node ./buildScripts/buildAll.mjs -f",
17
+ "build-es-modules" : "node ./buildScripts/buildESModules.mjs",
18
+ "build-themes" : "node ./buildScripts/buildThemes.mjs -f",
19
+ "build-threads" : "node ./buildScripts/webpack/buildThreads.mjs -f",
20
+ "convert-design-tokens" : "node ./buildScripts/convertDesignTokens.mjs",
21
+ "create-app" : "node ./buildScripts/createApp.mjs",
22
+ "create-app-minimal" : "node ./buildScripts/createAppMinimal.mjs",
23
+ "create-class" : "node ./buildScripts/createClass.mjs",
24
+ "create-component" : "node ./buildScripts/createComponent.mjs",
25
+ "generate-docs-json" : "node ./buildScripts/docs/jsdocx.mjs",
25
26
  "inject-package-version": "node ./buildScripts/injectPackageVersion.mjs",
26
- "server-start": "webpack serve -c ./buildScripts/webpack/webpack.server.config.mjs --open",
27
- "test": "echo \"Error: no test specified\" && exit 1",
28
- "watch-themes": "node ./buildScripts/watchThemes.mjs"
27
+ "server-start" : "webpack serve -c ./buildScripts/webpack/webpack.server.config.mjs --open",
28
+ "test" : "echo \"Error: no test specified\" && exit 1",
29
+ "watch-themes" : "node ./buildScripts/watchThemes.mjs"
29
30
  },
30
- "keywords": [
31
+ "keywords" : [
31
32
  "javascript",
32
33
  "frontend",
33
34
  "framework",
@@ -40,39 +41,41 @@
40
41
  "react-alternative",
41
42
  "angular-alternative"
42
43
  ],
43
- "author": "Tobias Uhlig",
44
- "license": "MIT",
45
- "bugs": {
44
+ "author" : "Tobias Uhlig",
45
+ "license" : "MIT",
46
+ "bugs" : {
46
47
  "url": "https://github.com/neomjs/neo/issues"
47
48
  },
48
- "homepage": "https://neomjs.com/",
49
+ "homepage" : "https://neomjs.com/",
49
50
  "devDependencies": {
50
51
  "@fortawesome/fontawesome-free": "^6.7.2",
51
- "autoprefixer": "^10.4.21",
52
- "chalk": "^5.4.1",
53
- "clean-webpack-plugin": "^4.0.0",
54
- "commander": "^14.0.0",
55
- "cssnano": "^7.0.7",
56
- "envinfo": "^7.14.0",
57
- "fs-extra": "^11.3.0",
58
- "highlightjs-line-numbers.js": "^2.9.0",
59
- "inquirer": "^12.6.2",
60
- "marked": "^15.0.12",
61
- "monaco-editor": "0.50.0",
62
- "neo-jsdoc": "1.0.1",
63
- "neo-jsdoc-x": "1.0.5",
64
- "postcss": "^8.5.3",
65
- "sass": "^1.89.0",
66
- "siesta-lite": "5.5.2",
67
- "url": "^0.11.4",
68
- "webpack": "^5.99.9",
69
- "webpack-cli": "^6.0.1",
70
- "webpack-dev-server": "^5.2.1",
71
- "webpack-hook-plugin": "^1.0.7",
72
- "webpack-node-externals": "^3.0.0"
52
+ "autoprefixer" : "^10.4.21",
53
+ "chalk" : "^5.4.1",
54
+ "clean-webpack-plugin" : "^4.0.0",
55
+ "commander" : "^14.0.0",
56
+ "cssnano" : "^7.0.7",
57
+ "envinfo" : "^7.14.0",
58
+ "fs-extra" : "^11.3.0",
59
+ "highlightjs-line-numbers.js" : "^2.9.0",
60
+ "html-minifier-terser" : "^7.2.0",
61
+ "inquirer" : "^12.6.3",
62
+ "marked" : "^15.0.12",
63
+ "monaco-editor" : "0.50.0",
64
+ "neo-jsdoc" : "1.0.1",
65
+ "neo-jsdoc-x" : "1.0.5",
66
+ "postcss" : "^8.5.4",
67
+ "sass" : "^1.89.1",
68
+ "siesta-lite" : "5.5.2",
69
+ "terser" : "^5.40.0",
70
+ "url" : "^0.11.4",
71
+ "webpack" : "^5.99.9",
72
+ "webpack-cli" : "^6.0.1",
73
+ "webpack-dev-server" : "^5.2.1",
74
+ "webpack-hook-plugin" : "^1.0.7",
75
+ "webpack-node-externals" : "^3.0.0"
73
76
  },
74
- "funding": {
77
+ "funding" : {
75
78
  "type": "GitHub Sponsors",
76
- "url": "https://github.com/sponsors/tobiu"
79
+ "url" : "https://github.com/sponsors/tobiu"
77
80
  }
78
81
  }
@@ -263,12 +263,12 @@ const DefaultConfig = {
263
263
  useVdomWorker: true,
264
264
  /**
265
265
  * buildScripts/injectPackageVersion.mjs will update this value
266
- * @default '9.5.0'
266
+ * @default '9.6.0'
267
267
  * @memberOf! module:Neo
268
268
  * @name config.version
269
269
  * @type String
270
270
  */
271
- version: '9.5.0'
271
+ version: '9.6.0'
272
272
  };
273
273
 
274
274
  Object.assign(DefaultConfig, {
package/src/Main.mjs CHANGED
@@ -253,7 +253,7 @@ class Main extends core.Base {
253
253
 
254
254
  // we need different publicPath values for the main thread inside the webpack based dist envs,
255
255
  // depending on the hierarchy level of the app entry point
256
- if (config.environment !== 'development') {
256
+ if (config.environment === 'dist/development' || config.environment === 'dist/production') {
257
257
  __webpack_require__.p = config.basePath.substring(6)
258
258
  }
259
259
 
@@ -223,11 +223,13 @@ class LivePreview extends Container {
223
223
  }
224
224
 
225
225
  let me = this,
226
+ {config} = Neo,
226
227
  container = me.getPreviewContainer(),
228
+ hasJsModules = config.environment === 'development' || config.environment === 'dist/esm',
227
229
  source = me.editorValue || me.value,
230
+ className = me.findLastClassName(source),
228
231
  cleanLines = [],
229
232
  moduleNameAndPath = [],
230
- className = me.findLastClassName(source),
231
233
  params = [],
232
234
  vars = [],
233
235
  codeString, promises;
@@ -240,7 +242,7 @@ class LivePreview extends Container {
240
242
  path = importMatch[2],
241
243
  index;
242
244
 
243
- if (Neo.config.environment !== 'development') {
245
+ if (!hasJsModules) {
244
246
  index = path.lastIndexOf('../');
245
247
 
246
248
  if (index === 0) {
@@ -586,11 +586,13 @@ class Container extends Component {
586
586
  * {"modules": [], "items": []}
587
587
  * See: https://github.com/neomjs/neo/blob/dev/examples/serverside/gridContainer/resources/data/grid-container.json
588
588
  * It is important to add modules which are not already imported inside your app yet.
589
- * @param {String} url
589
+ * @param {Object} data
590
+ * @param {Object} [data.options={}]
591
+ * @param {String} data.url
590
592
  * @returns {Promise<Object[]>}
591
593
  */
592
- async loadItems(url) {
593
- let response = await fetch(url),
594
+ async loadItems({options={}, url}) {
595
+ let response = await fetch(url, options),
594
596
  remoteData = await response.json();
595
597
 
596
598
  if (remoteData.modules?.length > 0) {
@@ -43,13 +43,18 @@ class Stylesheet extends Base {
43
43
  super.construct(config);
44
44
 
45
45
  let neoConfig = Neo.config,
46
+ env = neoConfig.environment,
46
47
  faPath;
47
48
 
48
49
  if (neoConfig.useFontAwesome) {
49
- if (neoConfig.environment === 'development') {
50
- faPath = neoConfig.basePath + 'node_modules/@fortawesome/fontawesome-free/css/all.min.css'
50
+ if (env === 'development' || env === 'dist/esm') {
51
+ faPath = neoConfig.basePath + 'node_modules/@fortawesome/fontawesome-free/css/all.min.css';
52
+
53
+ if (env === 'dist/esm') {
54
+ faPath = '../../' + faPath
55
+ }
51
56
  } else {
52
- faPath = neoConfig.basePath.substr(6) + 'resources/fontawesome-free/css/all.min.css'
57
+ faPath = neoConfig.basePath.substring(6) + 'resources/fontawesome-free/css/all.min.css'
53
58
  }
54
59
 
55
60
  this.createStyleSheet(null, null, faPath)
@@ -68,11 +73,15 @@ class Stylesheet extends Base {
68
73
  {themes} = config,
69
74
  folders = ['src', ...themes],
70
75
  env = config.environment,
71
- path = env.startsWith('dist/') ? '' : config.appPath.includes('docs') ? `../dist/${env}/` : `../../dist/${env}/`,
76
+ path = env.startsWith('dist/') ? '' : config.appPath.includes('docs') ? `../dist/${env}/` : `../../dist/${env}/`,
72
77
  rootPath = config.basePath.substring(6);
73
78
 
74
79
  document.body.classList.add(themes[0]);
75
80
 
81
+ if (env === 'dist/esm') {
82
+ path = '../../' + path;
83
+ }
84
+
76
85
  folders.forEach(folder => {
77
86
  if (folder.startsWith('neo-')) {
78
87
  folder = folder.substring(4)
@@ -100,6 +109,10 @@ class Stylesheet extends Base {
100
109
  promises = [],
101
110
  rootPath = config.basePath.substring(6);
102
111
 
112
+ if (env === 'dist/esm') {
113
+ path = '../../' + path;
114
+ }
115
+
103
116
  if (className.startsWith('Neo.')) {
104
117
  className = className.substring(4)
105
118
  }
@@ -39,15 +39,16 @@ class Api extends Base {
39
39
  *
40
40
  */
41
41
  load() {
42
- let {config} = Neo,
43
- path = config.remotesApiUrl;
42
+ let {config} = Neo,
43
+ hasJsModules = config.environment === 'development' || config.environment === 'dist/esm',
44
+ path = config.remotesApiUrl;
44
45
 
45
- // relative paths need a special treatment
46
+ // Relative paths need a special treatment
46
47
  if (!path.includes('http')) {
47
48
  path = config.appPath.split('/');
48
49
  path.pop();
49
50
  path = `${path.join('/')}/${config.remotesApiUrl}`;
50
- path = (config.environment === 'development' ? '../../' : './') + path
51
+ path = (hasJsModules ? '../../' : './') + path
51
52
  }
52
53
 
53
54
  fetch(path)
@@ -391,20 +391,22 @@ class Provider extends Base {
391
391
  * @param {String} value
392
392
  */
393
393
  getFormatterVariables(value) {
394
+ let {environment} = Neo.config;
395
+
394
396
  if (Neo.isFunction(value)) {
395
397
  value = value.toString()
396
398
  }
397
399
 
398
- if (Neo.config.environment === 'dist/production') {
399
- // see: https://github.com/neomjs/neo/issues/2371
400
- // inside dist/prod the formatter:
400
+ if (environment === 'dist/esm' || environment === 'dist/production') {
401
+ // See: https://github.com/neomjs/neo/issues/2371
402
+ // Inside dist/esm & dist/prod the formatter:
401
403
  // data => DateUtil.convertToyyyymmdd(data.currentDate)
402
404
  // will get minified to:
403
405
  // e=>s.Z.convertToyyyymmdd(e.currentDate)
404
- // the new strategy: find the first variable name => "e"
405
- // replace it with "data":
406
+ // The new strategy: find the first variable name => "e"
407
+ // Replace it with "data":
406
408
  // data=>s.Z.convertToyyyymmdd(data.currentDate)
407
- // from there we can use the dev mode regex again.
409
+ // From there we can use the dev mode regex again.
408
410
 
409
411
  let dataName = value.match(variableNameRegex)[0],
410
412
  variableRegExp = new RegExp(`(^|[^\\w.])(${dataName})(?!\\w)`, 'g');
@@ -300,7 +300,7 @@ class App extends Base {
300
300
 
301
301
  return import(
302
302
  /* webpackInclude: /(?:\/|\\)app.mjs$/ */
303
- /* webpackExclude: /(?:\/|\\)node_modules/ */
303
+ /* webpackExclude: /(?:\/|\\)(dist|node_modules)/ */
304
304
  /* webpackMode: "lazy" */
305
305
  `../../${path}.mjs`
306
306
  )
@@ -433,15 +433,15 @@ class App extends Base {
433
433
  onRegisterNeoConfig(msg) {
434
434
  super.onRegisterNeoConfig(msg);
435
435
 
436
- let config = Neo.config,
437
- {data} = msg,
438
- url = 'resources/theme-map.json';
436
+ let {config} = Neo,
437
+ {data} = msg,
438
+ url = 'resources/theme-map.json';
439
439
 
440
440
  Neo.windowConfigs = Neo.windowConfigs || {};
441
441
 
442
442
  Neo.windowConfigs[data.windowId] = data;
443
443
 
444
- if (config.environment === 'development') {
444
+ if (config.environment === 'development' || config.environment === 'dist/esm') {
445
445
  url = `../../${url}`
446
446
  }
447
447
 
@@ -5,8 +5,8 @@ import Message from './Message.mjs';
5
5
  import Observable from '../core/Observable.mjs';
6
6
  import RemoteMethodAccess from './mixin/RemoteMethodAccess.mjs';
7
7
 
8
- const NeoConfig = Neo.config,
9
- devMode = NeoConfig.environment === 'development';
8
+ const NeoConfig = Neo.config,
9
+ hasJsModules = NeoConfig.environment === 'development' || NeoConfig.environment === 'dist/esm';
10
10
 
11
11
  // Using ?. since SWs do not exist for http (only https)
12
12
  navigator.serviceWorker?.addEventListener('controllerchange', function() {
@@ -89,19 +89,19 @@ class Manager extends Base {
89
89
  */
90
90
  workers: {
91
91
  app: {
92
- fileName: devMode ? 'App.mjs' : 'appworker.js'
92
+ fileName: hasJsModules ? 'App.mjs' : 'appworker.js'
93
93
  },
94
94
  canvas: {
95
- fileName: devMode ? 'Canvas.mjs' : 'canvasworker.js'
95
+ fileName: hasJsModules ? 'Canvas.mjs' : 'canvasworker.js'
96
96
  },
97
97
  data: {
98
- fileName: devMode ? 'Data.mjs' : 'dataworker.js'
98
+ fileName: hasJsModules ? 'Data.mjs' : 'dataworker.js'
99
99
  },
100
100
  task: {
101
- fileName: devMode ? 'Task.mjs' : 'taskworker.js'
101
+ fileName: hasJsModules ? 'Task.mjs' : 'taskworker.js'
102
102
  },
103
103
  vdom: {
104
- fileName: devMode ? 'VDom.mjs' : 'vdomworker.js'
104
+ fileName: hasJsModules ? 'VDom.mjs' : 'vdomworker.js'
105
105
  }
106
106
  }
107
107
  }
@@ -168,7 +168,7 @@ class Manager extends Base {
168
168
  name = `neomjs-${fileName.substring(0, fileName.indexOf('.')).toLowerCase()}-worker`,
169
169
  isShared = me.sharedWorkersEnabled && NeoConfig.useSharedWorkers,
170
170
  cls = isShared ? SharedWorker : Worker,
171
- worker = devMode // todo: switch to the new syntax to create a worker from a JS module once browsers are ready
171
+ worker = hasJsModules
172
172
  ? new cls(filePath, {name, type: 'module'})
173
173
  : new cls(filePath, {name});
174
174
 
@@ -295,7 +295,7 @@ class Manager extends Base {
295
295
  */
296
296
  onWorkerError(e) {
297
297
  // starting a worker from a JS module will show JS errors in a correct way
298
- !devMode && console.log('Worker Error:', e)
298
+ !hasJsModules && console.log('Worker Error:', e)
299
299
  }
300
300
 
301
301
  /**