x4js 2.3.5 → 2.3.7
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/cli/build-options.mjs +1 -1
- package/cli/build.mjs +16 -18
- package/cli/config.mjs +52 -43
- package/cli/dev.mjs +16 -12
- package/cli/main.mjs +3 -2
- package/package.json +1 -1
- package/src/components/gridview/gridview.ts +6 -1
package/cli/build-options.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { sassPlugin } from "esbuild-sass-plugin";
|
|
|
4
4
|
import { lessPlugin } from "./less-plugin.mjs";
|
|
5
5
|
import { copyPlugin } from "./copy-plugin.mjs";
|
|
6
6
|
import { rawFilePlugin } from "./rawfile-plugin.mjs";
|
|
7
|
-
import { diagnosticsPlugin } from "./diagnostic.mjs";
|
|
7
|
+
import { diagnosticsPlugin } from "./diagnostic-plugin.mjs";
|
|
8
8
|
|
|
9
9
|
const cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const devClient = path.join(cliDir, "dev-client.js");
|
package/cli/build.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import { parseArgs } from "node:util";
|
|
3
3
|
import esbuild from "esbuild";
|
|
4
|
-
import { loadConfig } from "./config.mjs";
|
|
4
|
+
import { DEFAULT_CONFIG_FILE, expandEnv, loadConfig } from "./config.mjs";
|
|
5
5
|
import { createBuildOptions } from "./build-options.mjs";
|
|
6
6
|
import { info, success } from "./log.mjs";
|
|
7
|
-
import { printBuildError} from "./diagnostic-plugin.mjs";
|
|
7
|
+
import { printBuildError, printWarnings } from "./diagnostic-plugin.mjs";
|
|
8
8
|
|
|
9
9
|
export async function build(argv = [], root = process.cwd()) {
|
|
10
10
|
const { values, positionals } = parseArgs({
|
|
11
11
|
args: argv,
|
|
12
12
|
options: {
|
|
13
|
+
config: { type: "string" },
|
|
13
14
|
debug: { type: "boolean", default: false },
|
|
14
15
|
},
|
|
15
16
|
allowPositionals: true,
|
|
@@ -19,29 +20,26 @@ export async function build(argv = [], root = process.cwd()) {
|
|
|
19
20
|
if (positionals.length)
|
|
20
21
|
throw new Error(`Unexpected argument: ${positionals[0]}`);
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
+
const configArg = values.config ? expandEnv(values.config) : DEFAULT_CONFIG_FILE;
|
|
24
|
+
const config = loadConfig(root, configArg, process.env, { explicit: values.config !== undefined });
|
|
23
25
|
const mode = values.debug ? "debug" : "production";
|
|
24
26
|
|
|
25
27
|
info("mode", mode);
|
|
28
|
+
info("config", config.configFile);
|
|
26
29
|
info("outdir", config.outdir);
|
|
27
30
|
|
|
28
31
|
await fs.rm(config.outdir, { recursive: true, force: true });
|
|
29
32
|
await fs.mkdir(config.outdir, { recursive: true });
|
|
30
33
|
|
|
31
34
|
const started = performance.now();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
await printBuildError(error);
|
|
43
|
-
process.exitCode = 1;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
success("built", `${Math.round(performance.now() - started)}ms`);
|
|
35
|
+
try {
|
|
36
|
+
const result = await esbuild.build(createBuildOptions(config, mode));
|
|
37
|
+
if (result.warnings.length)
|
|
38
|
+
await printWarnings(result.warnings);
|
|
39
|
+
success("built", `${Math.round(performance.now() - started)}ms`);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
await printBuildError(error);
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
}
|
|
47
45
|
}
|
package/cli/config.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
+
export const DEFAULT_CONFIG_FILE = "x4.config.json";
|
|
5
|
+
|
|
4
6
|
const DEFAULTS = Object.freeze({
|
|
5
7
|
entryPoints: ["src/main.ts"],
|
|
6
8
|
outdir: "./bin",
|
|
@@ -36,94 +38,107 @@ export function resolvePath(root, value, env = process.env) {
|
|
|
36
38
|
: path.resolve(root, expanded);
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
export function resolveConfigFile(root, configFile = DEFAULT_CONFIG_FILE, env = process.env) {
|
|
42
|
+
return resolvePath(root, configFile, env);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function readJson(filename) {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(fs.readFileSync(filename, "utf8"));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new Error(`Cannot read config '${filename}': ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
39
54
|
function validateCopy(copy) {
|
|
40
55
|
if (!Array.isArray(copy))
|
|
41
|
-
throw new Error("
|
|
56
|
+
throw new Error("copy must be an array");
|
|
42
57
|
|
|
43
58
|
for (const item of copy) {
|
|
44
59
|
if (!item || typeof item !== "object" || typeof item.from !== "string" || typeof item.to !== "string")
|
|
45
|
-
throw new Error("Each
|
|
60
|
+
throw new Error("Each copy entry must contain string 'from' and 'to' fields");
|
|
46
61
|
if (path.isAbsolute(item.to))
|
|
47
|
-
throw new Error(`
|
|
62
|
+
throw new Error(`copy destination must be relative to outdir: '${item.to}'`);
|
|
48
63
|
const normalized = path.normalize(item.to);
|
|
49
64
|
if (normalized === ".." || normalized.startsWith(`..${path.sep}`))
|
|
50
|
-
throw new Error(`
|
|
65
|
+
throw new Error(`copy destination escapes outdir: '${item.to}'`);
|
|
51
66
|
}
|
|
52
67
|
}
|
|
53
68
|
|
|
54
|
-
export function loadConfig(root = process.cwd(), env = process.env) {
|
|
69
|
+
export function loadConfig(root = process.cwd(), configFile = DEFAULT_CONFIG_FILE, env = process.env, { explicit = false } = {}) {
|
|
55
70
|
root = path.resolve(root);
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
if (!fs.existsSync(packageFile))
|
|
59
|
-
throw new Error(`Cannot find package.json in '${root}'`);
|
|
71
|
+
const filename = resolveConfigFile(root, configFile, env);
|
|
60
72
|
|
|
61
|
-
let
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
throw new Error(`Cannot read package.json: ${error.message}`);
|
|
67
|
-
}
|
|
73
|
+
let cfg = {};
|
|
74
|
+
if (fs.existsSync(filename))
|
|
75
|
+
cfg = readJson(filename);
|
|
76
|
+
else if (explicit)
|
|
77
|
+
throw new Error(`Config file not found: ${filename}`);
|
|
68
78
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
throw new Error("package.json field 'x4' must be an object");
|
|
79
|
+
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg))
|
|
80
|
+
throw new Error("x4 config must be an object");
|
|
72
81
|
|
|
73
82
|
const entryPoints = cfg.entryPoints ?? DEFAULTS.entryPoints;
|
|
74
83
|
if (!Array.isArray(entryPoints) || !entryPoints.length || entryPoints.some((entry) => typeof entry !== "string"))
|
|
75
|
-
throw new Error("
|
|
84
|
+
throw new Error("entryPoints must be a non-empty array of strings");
|
|
76
85
|
|
|
77
86
|
const copy = cfg.copy ?? DEFAULTS.copy;
|
|
78
87
|
validateCopy(copy);
|
|
79
88
|
|
|
80
89
|
const external = cfg.external ?? DEFAULTS.external;
|
|
81
90
|
if (!Array.isArray(external) || external.some((entry) => typeof entry !== "string"))
|
|
82
|
-
throw new Error("
|
|
91
|
+
throw new Error("external must be an array of strings");
|
|
83
92
|
|
|
84
93
|
const define = cfg.define ?? DEFAULTS.define;
|
|
85
94
|
if (!define || typeof define !== "object" || Array.isArray(define))
|
|
86
|
-
throw new Error("
|
|
95
|
+
throw new Error("define must be an object");
|
|
87
96
|
if (Object.values(define).some((value) => typeof value !== "string"))
|
|
88
|
-
throw new Error("
|
|
97
|
+
throw new Error("define values must be strings containing esbuild define expressions");
|
|
89
98
|
|
|
90
99
|
const devCfg = cfg.dev ?? {};
|
|
91
100
|
if (!devCfg || typeof devCfg !== "object" || Array.isArray(devCfg))
|
|
92
|
-
throw new Error("
|
|
101
|
+
throw new Error("dev must be an object");
|
|
93
102
|
|
|
94
103
|
const host = devCfg.host ?? DEFAULTS.dev.host;
|
|
95
104
|
if (typeof host !== "string" || !host)
|
|
96
|
-
throw new Error("
|
|
105
|
+
throw new Error("dev.host must be a non-empty string");
|
|
97
106
|
|
|
98
107
|
const port = devCfg.port;
|
|
99
108
|
if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535))
|
|
100
|
-
throw new Error("
|
|
109
|
+
throw new Error("dev.port must be an integer between 0 and 65535");
|
|
101
110
|
|
|
102
111
|
const https = devCfg.https ?? DEFAULTS.dev.https;
|
|
103
112
|
if (typeof https !== "boolean")
|
|
104
|
-
throw new Error("
|
|
113
|
+
throw new Error("dev.https must be a boolean");
|
|
105
114
|
|
|
106
115
|
if (devCfg.tls !== undefined) {
|
|
107
116
|
if (!devCfg.tls || typeof devCfg.tls !== "object" || Array.isArray(devCfg.tls))
|
|
108
|
-
throw new Error("
|
|
117
|
+
throw new Error("dev.tls must be an object");
|
|
109
118
|
if (typeof devCfg.tls.cert !== "string" || typeof devCfg.tls.key !== "string")
|
|
110
|
-
throw new Error("
|
|
119
|
+
throw new Error("dev.tls requires string 'cert' and 'key' fields");
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
const outdirRaw = cfg.outdir ?? DEFAULTS.outdir;
|
|
114
123
|
if (typeof outdirRaw !== "string")
|
|
115
|
-
throw new Error("
|
|
124
|
+
throw new Error("outdir must be a string");
|
|
116
125
|
|
|
117
126
|
const outdir = resolvePath(root, outdirRaw, env);
|
|
118
127
|
if (outdir === root)
|
|
119
|
-
throw new Error("
|
|
128
|
+
throw new Error("outdir cannot be the project root");
|
|
120
129
|
if (outdir === path.parse(outdir).root)
|
|
121
|
-
throw new Error("
|
|
130
|
+
throw new Error("outdir cannot be a filesystem root");
|
|
131
|
+
|
|
132
|
+
let esbuildOptions = {};
|
|
133
|
+
if (cfg.esbuild !== undefined) {
|
|
134
|
+
if (!cfg.esbuild || typeof cfg.esbuild !== "object" || Array.isArray(cfg.esbuild))
|
|
135
|
+
throw new Error("esbuild must be an object");
|
|
136
|
+
esbuildOptions = { ...cfg.esbuild };
|
|
137
|
+
}
|
|
122
138
|
|
|
123
139
|
return {
|
|
124
140
|
root,
|
|
125
|
-
|
|
126
|
-
package: pkg,
|
|
141
|
+
configFile: filename,
|
|
127
142
|
entryPoints: entryPoints.map((entry) => resolvePath(root, entry, env)),
|
|
128
143
|
outdir,
|
|
129
144
|
copy: copy.map((item) => ({
|
|
@@ -132,26 +147,20 @@ export function loadConfig(root = process.cwd(), env = process.env) {
|
|
|
132
147
|
})),
|
|
133
148
|
external: [...external],
|
|
134
149
|
define: { ...define },
|
|
135
|
-
|
|
150
|
+
dev: {
|
|
136
151
|
host,
|
|
137
152
|
port,
|
|
138
153
|
https,
|
|
139
154
|
tls: devCfg.tls ? { ...devCfg.tls } : undefined,
|
|
140
155
|
},
|
|
141
|
-
esbuild:
|
|
142
|
-
if (cfg.esbuild === undefined)
|
|
143
|
-
return {};
|
|
144
|
-
if (!cfg.esbuild || typeof cfg.esbuild !== "object" || Array.isArray(cfg.esbuild))
|
|
145
|
-
throw new Error("x4.esbuild must be an object");
|
|
146
|
-
return { ...cfg.esbuild };
|
|
147
|
-
})(),
|
|
156
|
+
esbuild: esbuildOptions,
|
|
148
157
|
};
|
|
149
158
|
}
|
|
150
159
|
|
|
151
160
|
export function resolveTls(config, env = process.env) {
|
|
152
161
|
const tls = config.dev.tls;
|
|
153
162
|
if (!tls)
|
|
154
|
-
throw new Error("HTTPS is enabled but
|
|
163
|
+
throw new Error("HTTPS is enabled but dev.tls.cert/key are not configured");
|
|
155
164
|
|
|
156
165
|
const certfile = resolvePath(config.root, tls.cert, env);
|
|
157
166
|
const keyfile = resolvePath(config.root, tls.key, env);
|
package/cli/dev.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import esbuild from "esbuild";
|
|
6
|
-
import { loadConfig, resolveTls } from "./config.mjs";
|
|
6
|
+
import { DEFAULT_CONFIG_FILE, expandEnv, loadConfig, resolveConfigFile, resolveTls } from "./config.mjs";
|
|
7
7
|
import { createBuildOptions } from "./build-options.mjs";
|
|
8
8
|
import { info, success, failure } from "./log.mjs";
|
|
9
9
|
|
|
@@ -59,6 +59,7 @@ export async function dev(argv = [], root = process.cwd()) {
|
|
|
59
59
|
const { values, positionals } = parseArgs({
|
|
60
60
|
args: argv,
|
|
61
61
|
options: {
|
|
62
|
+
config: { type: "string" },
|
|
62
63
|
host: { type: "string" },
|
|
63
64
|
port: { type: "string" },
|
|
64
65
|
http: { type: "boolean", default: false },
|
|
@@ -71,12 +72,15 @@ export async function dev(argv = [], root = process.cwd()) {
|
|
|
71
72
|
|
|
72
73
|
if (positionals.length)
|
|
73
74
|
throw new Error(`Unexpected argument: ${positionals[0]}`);
|
|
74
|
-
|
|
75
75
|
if (values.port !== undefined && !/^\d+$/.test(values.port))
|
|
76
76
|
throw new Error("--port must be an integer");
|
|
77
77
|
|
|
78
|
+
const configArg = values.config ? expandEnv(values.config) : DEFAULT_CONFIG_FILE;
|
|
79
|
+
const explicitConfig = values.config !== undefined;
|
|
80
|
+
const watchedConfigFile = resolveConfigFile(root, configArg);
|
|
81
|
+
|
|
78
82
|
let context;
|
|
79
|
-
let
|
|
83
|
+
let configWatcher;
|
|
80
84
|
let stopping = false;
|
|
81
85
|
let restartTimer;
|
|
82
86
|
let restartChain = Promise.resolve();
|
|
@@ -96,7 +100,6 @@ export async function dev(argv = [], root = process.cwd()) {
|
|
|
96
100
|
|
|
97
101
|
if (port !== undefined)
|
|
98
102
|
serveOptions.port = port;
|
|
99
|
-
|
|
100
103
|
if (useHttps)
|
|
101
104
|
Object.assign(serveOptions, resolveTls(config));
|
|
102
105
|
|
|
@@ -109,6 +112,7 @@ export async function dev(argv = [], root = process.cwd()) {
|
|
|
109
112
|
const actualHost = result.hosts.includes(host) ? host : (result.hosts[0] ?? host);
|
|
110
113
|
const url = serverUrl(actualHost, result.port, useHttps);
|
|
111
114
|
info("mode", "dev");
|
|
115
|
+
info("config", config.configFile);
|
|
112
116
|
info("outdir", config.outdir);
|
|
113
117
|
success("server", url);
|
|
114
118
|
|
|
@@ -126,9 +130,9 @@ export async function dev(argv = [], root = process.cwd()) {
|
|
|
126
130
|
async function reload() {
|
|
127
131
|
let config;
|
|
128
132
|
try {
|
|
129
|
-
config = loadConfig(root);
|
|
133
|
+
config = loadConfig(root, configArg, process.env, { explicit: explicitConfig });
|
|
130
134
|
protocolConfig(config, values);
|
|
131
|
-
if (
|
|
135
|
+
if (values.https || (!values.http && config.dev.https))
|
|
132
136
|
resolveTls(config);
|
|
133
137
|
}
|
|
134
138
|
catch (error) {
|
|
@@ -147,8 +151,6 @@ export async function dev(argv = [], root = process.cwd()) {
|
|
|
147
151
|
}
|
|
148
152
|
catch (error) {
|
|
149
153
|
failure(`restart: ${error.message}`);
|
|
150
|
-
// The old context cannot be restored after dispose. Keep watching
|
|
151
|
-
// package.json so fixing the configuration starts dev again.
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
|
|
@@ -159,11 +161,13 @@ export async function dev(argv = [], root = process.cwd()) {
|
|
|
159
161
|
}, 150);
|
|
160
162
|
}
|
|
161
163
|
|
|
162
|
-
const initialConfig = loadConfig(root);
|
|
164
|
+
const initialConfig = loadConfig(root, configArg, process.env, { explicit: explicitConfig });
|
|
163
165
|
await start(initialConfig);
|
|
164
166
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
const watchDir = path.dirname(watchedConfigFile);
|
|
168
|
+
const watchName = path.basename(watchedConfigFile);
|
|
169
|
+
configWatcher = fs.watch(watchDir, { persistent: true }, (_event, filename) => {
|
|
170
|
+
if (filename === null || filename.toString() === watchName)
|
|
167
171
|
scheduleReload();
|
|
168
172
|
});
|
|
169
173
|
|
|
@@ -172,7 +176,7 @@ export async function dev(argv = [], root = process.cwd()) {
|
|
|
172
176
|
return;
|
|
173
177
|
stopping = true;
|
|
174
178
|
clearTimeout(restartTimer);
|
|
175
|
-
|
|
179
|
+
configWatcher?.close();
|
|
176
180
|
await restartChain.catch(() => {});
|
|
177
181
|
await context?.dispose();
|
|
178
182
|
}
|
package/cli/main.mjs
CHANGED
|
@@ -21,10 +21,11 @@ function printHelp(version) {
|
|
|
21
21
|
Usage:
|
|
22
22
|
x4js create <project> [--template <name>] [--no-install]
|
|
23
23
|
x4js templates
|
|
24
|
-
x4js dev [--host <host>] [--port <port>] [--http|--https] [--open]
|
|
25
|
-
x4js build [--debug]
|
|
24
|
+
x4js dev [--config <file>] [--host <host>] [--port <port>] [--http|--https] [--open]
|
|
25
|
+
x4js build [--config <file>] [--debug]
|
|
26
26
|
|
|
27
27
|
Options:
|
|
28
|
+
--config <file> Config file (default: x4.config.json)
|
|
28
29
|
-h, --help Show help
|
|
29
30
|
-v, --version Show version`);
|
|
30
31
|
}
|
package/package.json
CHANGED
|
@@ -33,7 +33,7 @@ import { _tr } from '../../core/core_i18n';
|
|
|
33
33
|
|
|
34
34
|
import "./gridview.module.scss"
|
|
35
35
|
|
|
36
|
-
export type CellRenderer = (rec: DataRecord) => Component;
|
|
36
|
+
export type CellRenderer = (rec: DataRecord, col_id: any) => Component;
|
|
37
37
|
export type CellClassifier = (data: any, rec: DataRecord, col: string ) => string; // return the cell computed class
|
|
38
38
|
|
|
39
39
|
type ColType = "number" | "money" | "checkbox" | "date" | "string" | "image" | "percent" | "icon" | "date-time";
|
|
@@ -694,6 +694,11 @@ export class Gridview<P extends GridviewProps = GridviewProps, E extends Gridvie
|
|
|
694
694
|
return null;
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
+
if( column.renderer ) {
|
|
698
|
+
const cell = column.renderer( rec, col );
|
|
699
|
+
return cell;
|
|
700
|
+
}
|
|
701
|
+
|
|
697
702
|
let cls = "";
|
|
698
703
|
if( column.classifier ) {
|
|
699
704
|
extra_cls.push( column.classifier( data, rec, col ) );
|