@rettangoli/fe 1.2.0 → 1.2.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/README.md CHANGED
@@ -38,7 +38,7 @@ rtgl fe watch # Start dev server
38
38
  - [Jempl](https://github.com/yuusoft-org/jempl) - Template engine
39
39
 
40
40
  **Build & Development:**
41
- - [Vite](https://vite.dev/) - Dev server and production bundling
41
+ - [Vite 8](https://vite.dev/) - Rolldown-powered production bundling and dev server
42
42
 
43
43
  **Browser Native:**
44
44
  - Web Components - Component encapsulation
@@ -47,7 +47,7 @@ rtgl fe watch # Start dev server
47
47
 
48
48
  ### Prerequisites
49
49
 
50
- - Node.js 18+ or Bun
50
+ - Node.js 20.19+, Node.js 22.12+, or Bun
51
51
  - A `rettangoli.config.yaml` file in your project root
52
52
 
53
53
  ### Setup
@@ -93,10 +93,11 @@ src/
93
93
 
94
94
  `@rettangoli/fe` uses Vite directly through the Node API, behind the existing FE CLI commands.
95
95
 
96
- - `rtgl fe build` uses `vite.build()` with a virtual entry module generated from configured component files.
97
- - `rtgl fe watch` uses `vite.createServer()` and serves the configured `outfile` path via middleware.
96
+ - `rtgl fe build` uses Vite 8's Rolldown-powered `vite.build()` with a virtual entry module generated from configured component files.
97
+ - `rtgl fe watch` uses `vite.createServer()`, warms the virtual entry during startup, and serves the configured `outfile` path via middleware.
98
98
  - FE runtime source is generated in memory (virtual module), so no temporary generated JS files are required.
99
99
  - Contract validation, YAML parsing, and template parsing still run before code generation.
100
+ - Component directories, setup files, and locale files are registered explicitly so watch mode also sees sources outside the served static root.
100
101
 
101
102
  Current Vite features used by FE:
102
103
 
@@ -106,7 +107,7 @@ Current Vite features used by FE:
106
107
  - `resolveId` + `load` for the FE virtual entry (`virtual:rettangoli-fe-entry`).
107
108
  - `handleHotUpdate` for FE file change detection.
108
109
  - `configureServer` for full-page reload and serving the configured output entry URL.
109
- - Rollup output control through Vite (`entryFileNames`, `chunkFileNames`, `assetFileNames`) to preserve CLI `outfile` behavior.
110
+ - Rolldown output control through Vite (`entryFileNames`, `chunkFileNames`, `assetFileNames`) to preserve CLI `outfile` behavior.
110
111
 
111
112
  Notes:
112
113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/fe",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Frontend framework for building reactive web components",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -22,6 +22,9 @@
22
22
  "directory": "packages/rettangoli-fe"
23
23
  },
24
24
  "license": "MIT",
25
+ "engines": {
26
+ "node": "^20.19.0 || >=22.12.0"
27
+ },
25
28
  "exports": {
26
29
  ".": "./src/index.js",
27
30
  "./cli": "./src/cli/index.js",
@@ -36,7 +39,7 @@
36
39
  "jempl": "1.0.1",
37
40
  "js-yaml": "^4.1.0",
38
41
  "snabbdom": "^3.6.2",
39
- "vite": "^6.3.5"
42
+ "vite": "^8.1.4"
40
43
  },
41
44
  "scripts": {
42
45
  "dev": "node ../rettangoli-cli/cli.js fe watch",
package/src/cli/build.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { performance } from "node:perf_hooks";
2
3
 
3
4
  import { build as viteBuild } from "vite";
4
5
 
@@ -9,8 +10,6 @@ import {
9
10
  import { emitI18nAssets, loadI18nBuildContext } from "./i18nBuild.js";
10
11
 
11
12
  const buildRettangoliFrontend = async (options = {}) => {
12
- console.log("running build with options", options);
13
-
14
13
  const {
15
14
  cwd = process.cwd(),
16
15
  dirs = ["./example"],
@@ -29,9 +28,14 @@ const buildRettangoliFrontend = async (options = {}) => {
29
28
  i18n,
30
29
  errorPrefix: "[Build]",
31
30
  });
31
+ const startedAt = performance.now();
32
+
33
+ console.log(`[Build] Building ${resolvedOutfile}...`);
32
34
 
33
35
  await viteBuild({
34
36
  configFile: false,
37
+ clearScreen: false,
38
+ logLevel: "warn",
35
39
  root: cwd,
36
40
  plugins: [
37
41
  createRettangoliFeVitePlugin({
@@ -45,10 +49,11 @@ const buildRettangoliFrontend = async (options = {}) => {
45
49
  build: {
46
50
  outDir: relativeOutDir,
47
51
  emptyOutDir: false,
48
- minify: development ? false : "esbuild",
49
- sourcemap: !!development,
52
+ minify: development ? false : "oxc",
53
+ sourcemap: false,
50
54
  target: "esnext",
51
- rollupOptions: {
55
+ reportCompressedSize: false,
56
+ rolldownOptions: {
52
57
  input: RETTANGOLI_FE_VIRTUAL_ENTRY_ID,
53
58
  output: {
54
59
  format: "es",
@@ -62,7 +67,8 @@ const buildRettangoliFrontend = async (options = {}) => {
62
67
 
63
68
  emitI18nAssets({ outDir, i18nContext });
64
69
 
65
- console.log(`Build complete. Output file: ${resolvedOutfile}`);
70
+ const durationMs = Math.round(performance.now() - startedAt);
71
+ console.log(`[Build] Complete in ${durationMs}ms.`);
66
72
  };
67
73
 
68
74
  export default buildRettangoliFrontend;
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
 
3
+ import { getAllFiles } from "../commonBuild.js";
3
4
  import { isSupportedComponentFile } from "./contracts.js";
4
5
  import { generateFrontendEntrySource } from "./frontendEntrySource.js";
5
6
  import {
@@ -55,6 +56,24 @@ export const createRettangoliFeVitePlugin = ({
55
56
  let currentCommand = "build";
56
57
  let devServer = null;
57
58
 
59
+ const getComponentFilePaths = () => {
60
+ return getAllFiles(resolvedDirs)
61
+ .filter((filePath) => isSupportedComponentFile(filePath));
62
+ };
63
+
64
+ const getEntryWatchPaths = ({ includeDirectories = false } = {}) => {
65
+ const i18nWatchPaths = includeDirectories
66
+ ? getI18nWatchPaths({ i18nContext })
67
+ : Object.values(i18nContext.localeFiles || {});
68
+
69
+ return [
70
+ ...(includeDirectories ? resolvedDirs : []),
71
+ resolvedSetup,
72
+ ...getComponentFilePaths(),
73
+ ...i18nWatchPaths,
74
+ ];
75
+ };
76
+
58
77
  const isTrackedFilePath = (value) => {
59
78
  const filePath = path.resolve(value);
60
79
  if (filePath === resolvedSetup) {
@@ -110,6 +129,13 @@ export const createRettangoliFeVitePlugin = ({
110
129
  if (id !== RESOLVED_VIRTUAL_ENTRY_ID) {
111
130
  return null;
112
131
  }
132
+
133
+ for (const filePath of getEntryWatchPaths()) {
134
+ if (typeof this.addWatchFile === "function") {
135
+ this.addWatchFile(filePath);
136
+ }
137
+ }
138
+
113
139
  return generateFrontendEntrySource({
114
140
  cwd,
115
141
  dirs,
@@ -121,10 +147,7 @@ export const createRettangoliFeVitePlugin = ({
121
147
  },
122
148
  configureServer(server) {
123
149
  devServer = server;
124
- const i18nWatchPaths = getI18nWatchPaths({ i18nContext });
125
- if (i18nWatchPaths.length > 0) {
126
- server.watcher.add(i18nWatchPaths);
127
- }
150
+ server.watcher.add(getEntryWatchPaths({ includeDirectories: true }));
128
151
 
129
152
  const serveI18nAsset = (req, res, next) => {
130
153
  if (!i18nContext.enabled || !normalizedPublicEntryPath) {
package/src/cli/watch.js CHANGED
@@ -2,7 +2,10 @@ import path from "node:path";
2
2
 
3
3
  import { createServer } from "vite";
4
4
 
5
- import { createRettangoliFeVitePlugin } from "./vitePlugin.js";
5
+ import {
6
+ RETTANGOLI_FE_VIRTUAL_ENTRY_ID,
7
+ createRettangoliFeVitePlugin,
8
+ } from "./vitePlugin.js";
6
9
 
7
10
  const toPosixPath = (value) => value.split(path.sep).join("/");
8
11
 
@@ -45,10 +48,12 @@ export const createWatchServer = async (options = {}) => {
45
48
  const { root, publicEntryPath } = resolveServeContext({ cwd, outfile });
46
49
 
47
50
  const server = await createServer({
51
+ clearScreen: false,
48
52
  configFile: false,
49
53
  root,
50
54
  server: {
51
55
  port,
56
+ strictPort: true,
52
57
  host: "0.0.0.0",
53
58
  allowedHosts: true,
54
59
  },
@@ -75,12 +80,13 @@ const startWatching = async (options = {}) => {
75
80
  } = options;
76
81
  const { root, publicEntryPath } = resolveServeContext({ cwd, outfile });
77
82
 
78
- console.log("watch root dir:", root);
79
- console.log("watch entry path:", publicEntryPath);
83
+ console.log(`[Watch] Root: ${root}`);
84
+ console.log(`[Watch] Entry: ${publicEntryPath}`);
80
85
 
81
86
  try {
82
87
  const server = await createWatchServer(options);
83
88
  await server.listen();
89
+ await server.transformRequest(RETTANGOLI_FE_VIRTUAL_ENTRY_ID);
84
90
  server.printUrls();
85
91
  if (enableCliShortcuts) {
86
92
  server.bindCLIShortcuts({ print: true });