@unsetsoft/ryunix-presets 1.0.26-canary.44 → 1.0.26-canary.45

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.
@@ -7,6 +7,7 @@ import defaultSettings from '../utils/config.js';
7
7
  import { resolveApp } from '../utils/index.js';
8
8
  import fs from 'fs';
9
9
  import path from 'path';
10
+ import { moduleImportUrl } from '../utils/moduleImportUrl.js';
10
11
  const loadResolvedSSGRoutes = async (buildDirectory) => {
11
12
  const serverBundleCandidates = [
12
13
  path.join(buildDirectory, 'server', 'app-router-server.bundle.js'),
@@ -16,7 +17,7 @@ const loadResolvedSSGRoutes = async (buildDirectory) => {
16
17
  if (!serverBundlePath)
17
18
  return null;
18
19
  try {
19
- const serverModule = await import(`file://${serverBundlePath}?update=${Date.now()}`);
20
+ const serverModule = await import(moduleImportUrl(serverBundlePath, true));
20
21
  if (typeof serverModule.resolveSSGPaths === 'function') {
21
22
  return await serverModule.resolveSSGPaths();
22
23
  }
@@ -0,0 +1,10 @@
1
+ import { pathToFileURL } from 'node:url';
2
+ /**
3
+ * Build a dynamic import URL from an absolute filesystem path.
4
+ * Uses pathToFileURL instead of `file://${path}` so webpack FileSystemInfo
5
+ * does not treat the template literal as a resolvable file: dependency.
6
+ */
7
+ export function moduleImportUrl(filePath, bustCache = false) {
8
+ const href = pathToFileURL(filePath).href;
9
+ return bustCache ? `${href}?update=${Date.now()}` : href;
10
+ }
@@ -9,6 +9,7 @@ import chalk from 'chalk';
9
9
  import { randomBytes } from 'crypto';
10
10
  import { resolvePageMetadata } from '@unsetsoft/ryunixjs';
11
11
  import { buildMetadataPublicPath, copyRouteMetadataAssets, fromMetadataAssetManifest, metadataAssetsToMeta, } from './routeMetadataFiles.js';
12
+ import { moduleImportUrl } from './moduleImportUrl.js';
12
13
  const ryunixGlobal = globalThis;
13
14
  /**
14
15
  * Import a file as ES module — works for both .mjs and .js files.
@@ -18,13 +19,13 @@ const ryunixGlobal = globalThis;
18
19
  */
19
20
  const importEsmFile = async (filePath) => {
20
21
  if (filePath.endsWith('.js') || filePath.endsWith('.cjs')) {
21
- return import(`file://${filePath}?update=${Date.now()}`);
22
+ return import(moduleImportUrl(filePath, true));
22
23
  }
23
24
  // For .js: copy to a temp .mjs so Node.js treats it as ESM without warnings
24
25
  const tmpPath = path.join(os.tmpdir(), `ryunix-ssg-${randomBytes(8).toString('hex')}.mjs`);
25
26
  try {
26
27
  fs.copyFileSync(filePath, tmpPath);
27
- return await import(`file://${tmpPath}`);
28
+ return await import(moduleImportUrl(tmpPath));
28
29
  }
29
30
  finally {
30
31
  try {
@@ -603,18 +604,15 @@ const buildSSG = async (routesConfig, config, buildDir, debug = false) => {
603
604
  userAgent: 'ryunix-ssg',
604
605
  };
605
606
  }
606
- const serverModule = await import(`file://${serverBundlePath}?update=${Date.now()}`);
607
+ const serverModule = await import(moduleImportUrl(serverBundlePath, true));
607
608
  AppRouterApp = serverModule.default?.default || serverModule.default;
608
609
  const ryunixCore = await import('@unsetsoft/ryunixjs');
609
610
  const Ryunix = ryunixCore.default || ryunixCore;
610
- ryunixGlobal.Ryunix = {
611
- renderToString: Ryunix.renderToString,
612
- renderToStringAsync: Ryunix.renderToStringAsync,
613
- createElement: Ryunix.createElement,
614
- getState: Ryunix.getState,
615
- };
616
- ryunixRenderToString = ryunixGlobal.Ryunix.renderToString ?? null;
617
- ryunixCreateElement = ryunixGlobal.Ryunix.createElement ?? null;
611
+ // User .ryx modules reference the Ryunix JSX pragma without importing it.
612
+ // Match ssrDevHandler: expose the full runtime, not a partial API stub.
613
+ ryunixGlobal.Ryunix = Ryunix;
614
+ ryunixRenderToString = Ryunix.renderToString;
615
+ ryunixCreateElement = Ryunix.createElement;
618
616
  }
619
617
  }
620
618
  catch (e) {
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import { prerenderRoute } from './ssg.js';
3
3
  import { resolveApp } from './index.js';
4
+ import { moduleImportUrl } from './moduleImportUrl.js';
4
5
  export async function renderDevRoute(req, res, devServer, dir, config) {
5
6
  if (req.method !== 'GET' || !req.headers.accept?.includes('text/html')) {
6
7
  return false;
@@ -45,7 +46,7 @@ export async function renderDevRoute(req, res, devServer, dir, config) {
45
46
  querySelector: () => null,
46
47
  getElementById: () => null,
47
48
  };
48
- const serverModule = await import(`file://${serverBundlePath}?update=${Date.now()}`);
49
+ const serverModule = await import(moduleImportUrl(serverBundlePath, true));
49
50
  AppRouterApp = serverModule.default?.default || serverModule.default;
50
51
  const ryunixCore = await import('@unsetsoft/ryunixjs');
51
52
  const Ryunix = ryunixCore.default || ryunixCore;
@@ -99,7 +100,8 @@ export async function renderDevRoute(req, res, devServer, dir, config) {
99
100
  const styleLinks = cssFiles
100
101
  .map((f) => `<link rel="stylesheet" href="/css/${f}" />`)
101
102
  .join('\n');
102
- if (styleLinks) {
103
+ const hasStylesheet = /<link[^>]+rel=["']stylesheet["']/i.test(html);
104
+ if (styleLinks && !hasStylesheet) {
103
105
  html = html.replace('</head>', `${styleLinks}\n</head>`);
104
106
  }
105
107
  }
@@ -20,6 +20,7 @@ import ApiRouterPlugin from './utils/ApiRouterPlugin.js';
20
20
  import { resolveEslintExtensions, resolveEslintFilePatterns, } from './utils/eslint-files.js';
21
21
  import { handleApiRequest } from './utils/apiHandler.js';
22
22
  import { renderDevRoute } from './utils/ssrDevHandler.js';
23
+ import { resolvePostcssPlugins } from './utils/postcss-config.js';
23
24
  import remarkGfm from 'remark-gfm';
24
25
  import remarkFrontmatter from 'remark-frontmatter';
25
26
  import remarkMdxFrontmatter from 'remark-mdx-frontmatter';
@@ -74,38 +75,7 @@ catch (_e) {
74
75
  presetsNodeModules = dirname(resolveApp(dir, 'package.json'));
75
76
  }
76
77
  }
77
- // A require() rooted at the user project — resolves user-installed packages (e.g. tailwind, postcss plugins)
78
- const projectRequire = createRequire(resolveApp(dir, 'package.json'));
79
- /**
80
- * Load postcss plugins from the user project's postcss.config.js
81
- * resolving each plugin name via projectRequire so they are found
82
- * in the user's node_modules even in pnpm monorepos.
83
- */
84
- const resolvePostcssPlugins = () => {
85
- const configPath = resolveApp(dir, 'postcss.config.js');
86
- if (!fs.existsSync(configPath))
87
- return [];
88
- try {
89
- const config = projectRequire(configPath);
90
- const plugins = config.plugins || {};
91
- if (Array.isArray(plugins))
92
- return plugins;
93
- // Object form: { 'plugin-name': options }
94
- return Object.entries(plugins).map(([name, opts]) => {
95
- const pluginFn = projectRequire(name);
96
- const fn = pluginFn.default || pluginFn;
97
- return opts && typeof opts === 'object' && Object.keys(opts).length > 0
98
- ? fn(opts)
99
- : fn();
100
- });
101
- }
102
- catch (e) {
103
- const message = e instanceof Error ? e.message : String(e);
104
- console.warn(`[Ryunix] Could not load postcss.config.js: ${message}`);
105
- return [];
106
- }
107
- };
108
- const postcssPlugins = resolvePostcssPlugins();
78
+ const postcssPlugins = await resolvePostcssPlugins(dir);
109
79
  const hasAppDir = fs.existsSync(resolveApp(dir, 'app')) ||
110
80
  fs.existsSync(resolveApp(dir, `${config.rootDir}/app`));
111
81
  const resolveRyunixStylePath = () => {
@@ -859,6 +829,8 @@ const serverConfig = {
859
829
  ...sharedWebpackConfig,
860
830
  name: 'server',
861
831
  target: 'node', // Compile for Node.js
832
+ // Prevent webpack-dev-server from injecting HMR entry into the Node bundle.
833
+ devServer: false,
862
834
  entry: resolveApp(dir, `${config.buildDir}/server/app/app-router-server.js`),
863
835
  output: {
864
836
  path: resolveApp(dir, `${config.buildDir}/server`),
@@ -901,7 +873,12 @@ const serverConfig = {
901
873
  },
902
874
  ],
903
875
  },
904
- plugins: getPlugins(true),
876
+ plugins: [
877
+ new webpack.ProvidePlugin({
878
+ Ryunix: '@unsetsoft/ryunixjs',
879
+ }),
880
+ ...getPlugins(true),
881
+ ],
905
882
  externals: [
906
883
  {
907
884
  ryunix: '@unsetsoft/ryunixjs',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unsetsoft/ryunix-presets",
3
3
  "description": "Package with presets for different development environments.",
4
- "version": "1.0.26-canary.44",
4
+ "version": "1.0.26-canary.45",
5
5
  "author": "Neyunse",
6
6
  "type": "module",
7
7
  "repository": {
@@ -174,6 +174,19 @@ export interface RyunixStyleResolvedConfig {
174
174
  font: RyunixStyleFontResolved
175
175
  }
176
176
 
177
+ export interface RyunixI18nCookieConfig {
178
+ name?: string
179
+ maxAgeSeconds?: number
180
+ }
181
+
182
+ /** Locale routing and message catalogs (`createI18n` / `createAppI18n`). */
183
+ export interface RyunixI18nConfig {
184
+ locales: string[]
185
+ defaultLocale: string
186
+ localeLabels?: Record<string, string>
187
+ cookie?: boolean | RyunixI18nCookieConfig
188
+ }
189
+
177
190
  /**
178
191
  * Modern options for `ryunix.config.js` (merged with defaults in `config.cjs`).
179
192
  */
@@ -203,6 +216,8 @@ export interface RyunixConfig {
203
216
  favicon?: boolean | string
204
217
  /** Transpiler for application sources: `"swc"` (default) or `"babel"`. */
205
218
  compiler?: 'swc' | 'babel'
219
+ /** Locale routing (`/[locale]/…`) and optional message catalogs. */
220
+ i18n?: RyunixI18nConfig
206
221
  /** Verbose Ryunix / webpack logging. */
207
222
  debug?: boolean
208
223
  eslint?: RyunixEslintConfig