@soleil-se/build-app 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/LICENSE.md +1 -1
- package/index.js +11 -1
- package/package.json +6 -4
- package/rollup/plugins/replaceReplaceAll.js +66 -0
- package/rollup/server.js +2 -0
- package/utils/files.js +47 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,10 @@ title: Changelog
|
|
|
4
4
|
|
|
5
5
|
Baseras på [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) och använder [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.4.1] - 2025-09-26
|
|
8
|
+
|
|
9
|
+
- Byt ut `.replaceAll` till motsvarande `.split().join()` eller `.replace()` för regex på serversidan då det inte stöds i Rhino.
|
|
10
|
+
|
|
7
11
|
## [2.4.0] - 2025-09-26
|
|
8
12
|
|
|
9
13
|
- Lägg till stöd för TypeScript, läs mer i [Readme](https://docs.soleil.se/build/app/#typescript).
|
package/LICENSE.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(c) Copyright
|
|
1
|
+
(c) Copyright 2025 Soleil AB, all rights reserved.
|
package/index.js
CHANGED
|
@@ -24,8 +24,18 @@ function getWatchOptions() {
|
|
|
24
24
|
return { paths, ignored };
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Get the minimum Sitevision version based on the @sitevision/api package version.
|
|
29
|
+
* @returns {Promise<string>} The minimum Sitevision version.
|
|
30
|
+
*/
|
|
31
|
+
async function getMinimumSitevisionVersion() {
|
|
32
|
+
const packageJson = await fse.readJSON('./package.json');
|
|
33
|
+
return (packageJson.dependencies['@sitevision/api'] || '2025.4.1').replace(/^[\^~]/, '');
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
async function main() {
|
|
28
37
|
const manifest = await readManifest();
|
|
38
|
+
const minimumSitevisionVersion = await getMinimumSitevisionVersion();
|
|
29
39
|
const zipPath = `./dist/${manifest.id}-${manifest.version}.zip`;
|
|
30
40
|
|
|
31
41
|
logTimestamp(`${manifest.name} (${manifest.id})`);
|
|
@@ -84,7 +94,7 @@ async function main() {
|
|
|
84
94
|
dest: './dist/src/config/global',
|
|
85
95
|
cache: args.cache,
|
|
86
96
|
}), () => fse.existsSync('./config_global')),
|
|
87
|
-
task('files', files({ manifest, dest: './dist' })),
|
|
97
|
+
task('files', files({ manifest, minimumSitevisionVersion, dest: './dist' })),
|
|
88
98
|
task('zip', zip({ src: './dist/src', dest: zipPath })),
|
|
89
99
|
];
|
|
90
100
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soleil-se/build-app",
|
|
3
3
|
"description": "Script for building WebApps, RESTApps and Widgets with Svelte in Sitevision.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"build-app": "./bin/index.js",
|
|
7
7
|
"sv-app-build": "./bin/index.js"
|
|
@@ -51,14 +51,16 @@
|
|
|
51
51
|
"rollup-plugin-postcss": "4.0.2",
|
|
52
52
|
"rollup-plugin-string": "3.0.0",
|
|
53
53
|
"rollup-plugin-svelte": "7.2.3",
|
|
54
|
+
"semver": "^7.7.2",
|
|
54
55
|
"slash": "5.1.0",
|
|
56
|
+
"magic-string": "^0.30.19",
|
|
55
57
|
"svelte-preprocess": "6.0.3",
|
|
56
58
|
"tslib": "^2.8.1",
|
|
57
|
-
"@soleil-se/build-
|
|
58
|
-
"@soleil-se/build-
|
|
59
|
+
"@soleil-se/build-utils": "^1.8.3",
|
|
60
|
+
"@soleil-se/build-config": "^1.3.1"
|
|
59
61
|
},
|
|
60
62
|
"devDependencies": {
|
|
61
|
-
"svelte": "^5.
|
|
63
|
+
"svelte": "^5.39.6",
|
|
62
64
|
"typescript": "^5.9.2"
|
|
63
65
|
},
|
|
64
66
|
"publishConfig": {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Replace `.replaceAll(string, replacement)` with `.split(string).join(replacement)`
|
|
5
|
+
* @param {string} code - The input code to transform
|
|
6
|
+
* @param {Function} callback - The callback with start, end, and replacement
|
|
7
|
+
*/
|
|
8
|
+
function replaceStringUsage(code, callback) {
|
|
9
|
+
const stringRegex = /((?:\w+(?:\.\w+)*|\w+\([^)]*\)|[a-zA-Z_$][\w$]*(?:\[[^\]]*\])*))\.replaceAll\((['"`])(.*?)\2,\s*(.*?)\)/g;
|
|
10
|
+
let match;
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line no-cond-assign
|
|
13
|
+
while ((match = stringRegex.exec(code)) !== null) {
|
|
14
|
+
const [fullMatch, expression, quote, searchValue, replaceValue] = match;
|
|
15
|
+
const replacement = `${expression}.split(${quote}${searchValue}${quote}).join(${replaceValue})`;
|
|
16
|
+
callback({
|
|
17
|
+
start: match.index,
|
|
18
|
+
end: match.index + fullMatch.length,
|
|
19
|
+
replacement,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Replace `.replaceAll(regex, replacement)` with `.replace(regex, replacement)`
|
|
26
|
+
* @param {string} code - The input code to transform
|
|
27
|
+
* @param {Function} callback - The callback with start, end, and replacement
|
|
28
|
+
*/
|
|
29
|
+
function replaceRegexUsage(code, callback) {
|
|
30
|
+
const regexPattern = /\.replaceAll\((\/.+?\/[gimuy]*),\s*(.*?)\)/g;
|
|
31
|
+
let match;
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line no-cond-assign
|
|
34
|
+
while ((match = regexPattern.exec(code)) !== null) {
|
|
35
|
+
const [fullMatch, regexPart, replaceValue] = match;
|
|
36
|
+
const replacement = `.replace(${regexPart}, ${replaceValue})`;
|
|
37
|
+
callback({
|
|
38
|
+
start: match.index,
|
|
39
|
+
end: match.index + fullMatch.length,
|
|
40
|
+
replacement,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default function replaceReplaceAll() {
|
|
46
|
+
return {
|
|
47
|
+
name: 'replace-replaceall',
|
|
48
|
+
transform(code) {
|
|
49
|
+
if (code.includes('.replaceAll(')) {
|
|
50
|
+
const magicString = new MagicString(code);
|
|
51
|
+
replaceStringUsage(code, ({ start, end, replacement }) => {
|
|
52
|
+
magicString.overwrite(start, end, replacement);
|
|
53
|
+
});
|
|
54
|
+
replaceRegexUsage(code, ({ start, end, replacement }) => {
|
|
55
|
+
magicString.overwrite(start, end, replacement);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
code: magicString.toString(),
|
|
60
|
+
map: magicString.generateMap({ hires: true }),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
package/rollup/server.js
CHANGED
|
@@ -16,6 +16,7 @@ import getStringPlugin from './api/getStringPlugin.js';
|
|
|
16
16
|
import getTypescriptPlugin from './api/getTypescriptPlugin.js';
|
|
17
17
|
|
|
18
18
|
import sitevision from './plugins/sitevision.js';
|
|
19
|
+
import replaceReplaceAll from './plugins/replaceReplaceAll.js';
|
|
19
20
|
|
|
20
21
|
export default function rollupServer({
|
|
21
22
|
debug,
|
|
@@ -44,6 +45,7 @@ export default function rollupServer({
|
|
|
44
45
|
getSveltePlugin({ input: inputs[0], extractCss, ssr: true }),
|
|
45
46
|
getPostcssPlugin({ extractCss, debug }),
|
|
46
47
|
getBabelServerPlugin(),
|
|
48
|
+
replaceReplaceAll(),
|
|
47
49
|
getTerserPlugin({ debug }),
|
|
48
50
|
];
|
|
49
51
|
|
package/utils/files.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fse from 'fs-extra';
|
|
2
|
+
import semver from 'semver';
|
|
2
3
|
|
|
3
4
|
const indexContent = `(function() {
|
|
4
5
|
'use strict';
|
|
@@ -13,20 +14,35 @@ const indexContent = `(function() {
|
|
|
13
14
|
* @returns {Promise<void>}
|
|
14
15
|
*/
|
|
15
16
|
async function removeHydrationMarkers(src) {
|
|
16
|
-
if(fse.existsSync(src)) {
|
|
17
|
+
if (fse.existsSync(src)) {
|
|
17
18
|
const content = await fse.readFile(src);
|
|
18
19
|
await fse.writeFile(src, content.toString().replace(/(\\x3c!----\\x3e|\\x3c!--\[--\\x3e|\\x3c!--]--\\x3e)/gm, ''));
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Create main.js if not present and needed.
|
|
25
|
+
* @param {Object} params - The parameters.
|
|
26
|
+
* @param {Object} params.minimumSitevisionVersion - The minimum Sitevision version.
|
|
27
|
+
* @param {string} params.dest - The destination directory.
|
|
28
|
+
* @returns {Promise<void>}
|
|
29
|
+
*/
|
|
30
|
+
async function createMain({ minimumSitevisionVersion, dest }) {
|
|
23
31
|
const src = `${dest}/src/main.js`;
|
|
24
32
|
if (!fse.existsSync(src)) {
|
|
25
33
|
await removeHydrationMarkers(`${dest}/src/index.js`);
|
|
26
|
-
|
|
34
|
+
// An empty main.js is not required in Sitevision 2025.07.1 or later.
|
|
35
|
+
if (!minimumSitevisionVersion || semver.lt(minimumSitevisionVersion, '2025.7.1')) {
|
|
36
|
+
await fse.writeFile(src, '');
|
|
37
|
+
}
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Create index.js if not present.
|
|
43
|
+
* @param {string} dest - The destination directory.
|
|
44
|
+
* @returns {Promise<void>}
|
|
45
|
+
*/
|
|
30
46
|
async function createIndex(dest) {
|
|
31
47
|
const src = `${dest}/src/index.js`;
|
|
32
48
|
if (!fse.existsSync(src)) {
|
|
@@ -34,17 +50,36 @@ async function createIndex(dest) {
|
|
|
34
50
|
}
|
|
35
51
|
}
|
|
36
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Check if two files have the same content.
|
|
55
|
+
* @param {string} path1 - The path to the first file.
|
|
56
|
+
* @param {string} path2 - The path to the second file.
|
|
57
|
+
* @returns {Promise<boolean>}
|
|
58
|
+
*/
|
|
37
59
|
async function hasSameContent(path1, path2) {
|
|
38
60
|
if (!fse.existsSync(path1) || !fse.existsSync(path2)) return false;
|
|
39
61
|
const [content1, content2] = await Promise.all([fse.readFile(path1), fse.readFile(path2)]);
|
|
40
62
|
return Buffer.compare(content1, content2) === 0;
|
|
41
63
|
}
|
|
42
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Move a file if it exists.
|
|
67
|
+
* @param {string} src - The source file path.
|
|
68
|
+
* @param {string} dest - The destination file path.
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
*/
|
|
43
71
|
async function moveFileIfExists(src, dest) {
|
|
44
72
|
if (!fse.existsSync(src)) return Promise.resolve();
|
|
45
73
|
return fse.move(src, dest, { overwrite: true });
|
|
46
74
|
}
|
|
47
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Manage CSS files for the app.
|
|
78
|
+
* @param {Object} params - The parameters.
|
|
79
|
+
* @param {string} params.type - The app type.
|
|
80
|
+
* @param {string} params.dest - The destination directory.
|
|
81
|
+
* @returns {Promise<void>}
|
|
82
|
+
*/
|
|
48
83
|
async function manageCss({ type, dest }) {
|
|
49
84
|
const serverOutput = `${dest}/src/index.css`;
|
|
50
85
|
const clientOutput = `${dest}/src/main.css`;
|
|
@@ -63,10 +98,17 @@ async function manageCss({ type, dest }) {
|
|
|
63
98
|
]);
|
|
64
99
|
}
|
|
65
100
|
|
|
66
|
-
|
|
101
|
+
/**
|
|
102
|
+
* File management tasks for the app.
|
|
103
|
+
* @param {Object} params - The parameters.
|
|
104
|
+
* @param {Object} params.manifest - The app manifest.
|
|
105
|
+
* @param {string} params.dest - The destination directory.
|
|
106
|
+
* @returns {Promise<void>}
|
|
107
|
+
*/
|
|
108
|
+
export default function files({ manifest, minimumSitevisionVersion, dest }) {
|
|
67
109
|
const type = manifest.type.toLowerCase();
|
|
68
110
|
if (type === 'restapp') return manageCss({ type, dest });
|
|
69
111
|
return () => Promise.all(
|
|
70
|
-
[createMain(dest), createIndex(dest), manageCss({ type, dest })],
|
|
112
|
+
[createMain({ minimumSitevisionVersion, dest }), createIndex(dest), manageCss({ type, dest })],
|
|
71
113
|
);
|
|
72
114
|
}
|