@wuchale/jsx 0.5.1 → 0.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defaultGenerateLoadID, defaultHeuristic, deepMergeObjects } from 'wuchale';
2
- import { adapter as vanillaAdapter, initRuntimeStmt } from 'wuchale/adapter-vanilla';
2
+ import { adapter as vanillaAdapter } from 'wuchale/adapter-vanilla';
3
3
  import { JSXTransformer } from "./transformer.js";
4
4
  import { getDependencies } from 'wuchale/adapter-utils';
5
5
  const ignoreElements = ['style', 'path'];
@@ -18,6 +18,41 @@ const jsxHeuristic = (msgStr, details) => {
18
18
  }
19
19
  return true;
20
20
  };
21
+ const defaultRuntime = {
22
+ useReactive: ({ funcName, nested }) => {
23
+ const inTopLevel = funcName == null;
24
+ const insideReactive = !inTopLevel && !nested && ((funcName.startsWith('use') && funcName.length > 3) || /[A-Z]/.test(funcName[0]));
25
+ return {
26
+ init: inTopLevel ? null : insideReactive,
27
+ use: insideReactive
28
+ };
29
+ },
30
+ reactive: {
31
+ importName: 'default',
32
+ wrapInit: expr => expr,
33
+ wrapUse: expr => expr,
34
+ },
35
+ plain: {
36
+ importName: 'get',
37
+ wrapInit: expr => expr,
38
+ wrapUse: expr => expr,
39
+ },
40
+ };
41
+ const defaultRuntimeSolid = {
42
+ ...defaultRuntime,
43
+ useReactive: ({ funcName }) => {
44
+ const inTopLevel = funcName == null;
45
+ return {
46
+ init: inTopLevel ? true : null, // init only in top level
47
+ use: true, // always use reactive
48
+ };
49
+ },
50
+ reactive: {
51
+ importName: 'default',
52
+ wrapInit: expr => `() => ${expr}`,
53
+ wrapUse: expr => `${expr}()`
54
+ }
55
+ };
21
56
  const defaultArgs = {
22
57
  files: { include: 'src/**/*.{js,ts,jsx,tsx}', ignore: '**/*.d.ts' },
23
58
  catalog: './src/locales/{locale}',
@@ -27,13 +62,17 @@ const defaultArgs = {
27
62
  bundleLoad: false,
28
63
  generateLoadID: defaultGenerateLoadID,
29
64
  writeFiles: {},
65
+ runtime: defaultRuntime,
30
66
  variant: 'default',
31
67
  };
32
68
  export const adapter = (args = defaultArgs) => {
33
- const { heuristic, pluralsFunc, variant, ...rest } = deepMergeObjects(args, defaultArgs);
69
+ let { heuristic, pluralsFunc, variant, runtime, ...rest } = deepMergeObjects(args, defaultArgs);
70
+ if (variant === 'solidjs' && args.runtime == null) {
71
+ runtime = defaultRuntimeSolid;
72
+ }
34
73
  return {
35
74
  transform: ({ content, filename, index, header }) => {
36
- return new JSXTransformer(content, filename, index, heuristic, pluralsFunc, initRuntimeStmt(header.expr)).transformJx(header.head, variant);
75
+ return new JSXTransformer(content, filename, index, heuristic, pluralsFunc, header.expr, runtime).transformJx(header.head, variant);
37
76
  },
38
77
  loaderExts: ['.js', '.ts'],
39
78
  defaultLoaders: async () => {
@@ -56,6 +95,7 @@ export const adapter = (args = defaultArgs) => {
56
95
  }
57
96
  return new URL(`../src/loaders/${loader}.js`, import.meta.url).pathname;
58
97
  },
98
+ runtime,
59
99
  ...rest,
60
100
  docsUrl: 'https://wuchale.dev/adapters/jsx'
61
101
  };
@@ -1,10 +1,10 @@
1
- import { type Program } from "acorn";
2
1
  import { Message } from 'wuchale';
3
2
  import type * as JX from 'estree-jsx';
3
+ import type * as Estree from 'acorn';
4
4
  import { Transformer } from 'wuchale/adapter-vanilla';
5
- import type { IndexTracker, HeuristicFunc, TransformOutput, CommentDirectives } from 'wuchale';
5
+ import type { IndexTracker, HeuristicFunc, TransformOutput, CommentDirectives, RuntimeConf, CatalogExpr } from 'wuchale';
6
6
  import { MixedVisitor } from "wuchale/adapter-utils";
7
- export declare function parseScript(content: string): [Program, JX.Comment[][]];
7
+ export declare function parseScript(content: string): [Estree.Program, Estree.Comment[][]];
8
8
  type MixedNodesTypes = JX.JSXElement | JX.JSXFragment | JX.JSXText | JX.JSXExpressionContainer | JX.JSXSpreadChild;
9
9
  export type JSXLib = 'default' | 'solidjs';
10
10
  export declare class JSXTransformer extends Transformer {
@@ -14,7 +14,7 @@ export declare class JSXTransformer extends Transformer {
14
14
  lastVisitIsComment: boolean;
15
15
  currentElementI: number;
16
16
  mixedVisitor: MixedVisitor<MixedNodesTypes>;
17
- constructor(content: string, filename: string, index: IndexTracker, heuristic: HeuristicFunc, pluralsFunc: string, initRuntime: string);
17
+ constructor(content: string, filename: string, index: IndexTracker, heuristic: HeuristicFunc, pluralsFunc: string, catalogExpr: CatalogExpr, rtConf: RuntimeConf);
18
18
  initMixedVisitor: () => MixedVisitor<MixedNodesTypes>;
19
19
  visitChildrenJ: (node: JX.JSXElement | JX.JSXFragment) => Message[];
20
20
  visitNameJSXNamespacedName: (node: JX.JSXNamespacedName) => string;
@@ -29,7 +29,7 @@ export declare class JSXTransformer extends Transformer {
29
29
  visitJSXAttribute: (node: JX.JSXAttribute) => Message[];
30
30
  visitJSXSpreadAttribute: (node: JX.JSXSpreadAttribute) => Message[];
31
31
  visitJSXEmptyExpression: (node: JX.JSXEmptyExpression) => Message[];
32
- visitJx: (node: JX.Node | JX.JSXSpreadChild | Program) => Message[];
32
+ visitJx: (node: JX.Node | JX.JSXSpreadChild | Estree.Program) => Message[];
33
33
  transformJx: (headerHead: string, lib: JSXLib) => TransformOutput;
34
34
  }
35
35
  export {};
@@ -1,10 +1,10 @@
1
- import MagicString from "magic-string";
2
- import { Parser } from "acorn";
1
+ import MagicString from 'magic-string';
2
+ import { Parser } from 'acorn';
3
3
  import { Message } from 'wuchale';
4
4
  import { tsPlugin } from '@sveltejs/acorn-typescript';
5
5
  import jsx from 'acorn-jsx';
6
6
  import { Transformer, scriptParseOptionsWithComments } from 'wuchale/adapter-vanilla';
7
- import { nonWhitespaceText, MixedVisitor, runtimeVars } from "wuchale/adapter-utils";
7
+ import { nonWhitespaceText, MixedVisitor } from "wuchale/adapter-utils";
8
8
  const JsxParser = Parser.extend(tsPlugin(), jsx());
9
9
  export function parseScript(content) {
10
10
  const [opts, comments] = scriptParseOptionsWithComments();
@@ -20,11 +20,12 @@ export class JSXTransformer extends Transformer {
20
20
  lastVisitIsComment = false;
21
21
  currentElementI = 0;
22
22
  mixedVisitor;
23
- constructor(content, filename, index, heuristic, pluralsFunc, initRuntime) {
24
- super(content, filename, index, heuristic, pluralsFunc, initRuntime);
23
+ constructor(content, filename, index, heuristic, pluralsFunc, catalogExpr, rtConf) {
24
+ super(content, filename, index, heuristic, pluralsFunc, catalogExpr, rtConf);
25
25
  }
26
26
  initMixedVisitor = () => new MixedVisitor({
27
27
  mstr: this.mstr,
28
+ vars: this.vars,
28
29
  getRange: node => ({
29
30
  // @ts-expect-error
30
31
  start: node.start,
@@ -59,15 +60,15 @@ export class JSXTransformer extends Transformer {
59
60
  else {
60
61
  toAppend = ', ';
61
62
  }
62
- this.mstr.appendRight(childStart, `${toAppend}${haveCtx ? runtimeVars.nestCtx : '()'} => `);
63
+ this.mstr.appendRight(childStart, `${toAppend}${haveCtx ? this.vars().nestCtx : '()'} => `);
63
64
  }
64
65
  let begin = `]} ctx=`;
65
66
  if (this.inCompoundText) {
66
- begin += `{${runtimeVars.nestCtx}} nest`;
67
+ begin += `{${this.vars().nestCtx}} nest`;
67
68
  }
68
69
  else {
69
70
  const index = this.index.get(msgInfo.toKey());
70
- begin += `{${runtimeVars.rtCtx}(${index})}`;
71
+ begin += `{${this.vars().rtCtx}(${index})}`;
71
72
  }
72
73
  let end = ' />';
73
74
  if (hasExprs) {
@@ -123,7 +124,7 @@ export class JSXTransformer extends Transformer {
123
124
  // @ts-expect-error
124
125
  node.start + startWh,
125
126
  // @ts-expect-error
126
- node.end - endWh, `{${runtimeVars.rtTrans}(${this.index.get(msgInfo.toKey())})}`);
127
+ node.end - endWh, `{${this.vars().rtTrans}(${this.index.get(msgInfo.toKey())})}`);
127
128
  return [msgInfo];
128
129
  };
129
130
  visitJSXFragment = (node) => this.visitChildrenJ(node);
@@ -139,6 +140,9 @@ export class JSXTransformer extends Transformer {
139
140
  return this.visit(node.expression);
140
141
  };
141
142
  visitJSXAttribute = (node) => {
143
+ if (node.value == null) {
144
+ return [];
145
+ }
142
146
  if (node.value.type !== 'Literal') {
143
147
  return this.visitJx(node.value);
144
148
  }
@@ -165,7 +169,7 @@ export class JSXTransformer extends Transformer {
165
169
  // @ts-expect-error
166
170
  value.start,
167
171
  // @ts-expect-error
168
- value.end, `{${runtimeVars.rtTrans}(${this.index.get(msgInfo.toKey())})}`);
172
+ value.end, `{${this.vars().rtTrans}(${this.index.get(msgInfo.toKey())})}`);
169
173
  return [msgInfo];
170
174
  };
171
175
  visitJSXSpreadAttribute = (node) => this.visit(node.argument);
@@ -212,13 +216,13 @@ export class JSXTransformer extends Transformer {
212
216
  if (!msgs.length) {
213
217
  return this.finalize(msgs, 0);
214
218
  }
215
- let devInit = '';
216
219
  const headerFin = [
217
220
  `import ${rtComponent} from "@wuchale/jsx/runtime${lib === 'solidjs' ? '.solid' : ''}.jsx"`,
218
221
  headerHead,
219
- devInit,
222
+ this.initRuntime(this.filename, null, null, {}),
220
223
  ].join('\n');
221
- this.mstr.appendRight(0, headerFin + '\n');
222
- return this.finalize(msgs, 0);
224
+ const bodyStart = this.getRealBodyStart(ast.body);
225
+ this.mstr.appendRight(bodyStart, headerFin + '\n');
226
+ return this.finalize(msgs, bodyStart);
223
227
  };
224
228
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wuchale/jsx",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Protobuf-like i18n from plain code: JSX adapter",
5
5
  "scripts": {
6
6
  "dev": "tsc --watch",
@@ -72,7 +72,7 @@
72
72
  "@sveltejs/acorn-typescript": "^1.0.5",
73
73
  "acorn": "^8.15.0",
74
74
  "acorn-jsx": "^5.3.2",
75
- "wuchale": "^0.14.2"
75
+ "wuchale": "^0.15.0"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@types/estree-jsx": "^1.0.5",
@@ -5,7 +5,9 @@
5
5
 
6
6
  import { useState, useEffect } from 'react'
7
7
 
8
- const callbacks = new Set()
8
+ let locale = 'en'
9
+
10
+ const callbacks = new Set([(/** @type {string} */ loc) => {locale = loc}])
9
11
 
10
12
  /**
11
13
  * @param {string} locale
@@ -19,7 +21,12 @@ export function setLocale(locale) {
19
21
  export default (/** @type {{[locale: string]: import('wuchale/runtime').CatalogModule }} */ catalogs) => {
20
22
  const [locale, setLocale] = useState('en')
21
23
  useEffect(() => {
22
- callbacks.add((/** @type {string} */ locale) => setLocale(locale))
24
+ const cb = (/** @type {string} */ locale) => setLocale(locale)
25
+ callbacks.add(cb)
26
+ return () => callbacks.delete(cb)
23
27
  })
24
28
  return catalogs[locale]
25
29
  }
30
+
31
+ // non-reactive
32
+ export const get = (/** @type {{[locale: string]: import('wuchale/runtime').CatalogModule }} */ catalogs) => catalogs[locale]
@@ -10,8 +10,11 @@ import { useState, useEffect } from 'react'
10
10
  const callbacks = {}
11
11
  const store = {}
12
12
 
13
+ // non-reactive
14
+ export const get = (/** @type {string} */ loadID) => store[loadID]
15
+
13
16
  const collection = {
14
- get: loadID => store[loadID],
17
+ get,
15
18
  set: (/** @type {string} */ loadID, /** @type {import('wuchale/runtime').CatalogModule} */ catalog) => {
16
19
  store[loadID] = catalog // for when useEffect hasn't run yet
17
20
  callbacks[loadID]?.(catalog)
@@ -20,13 +23,11 @@ const collection = {
20
23
 
21
24
  registerLoaders(key, loadCatalog, loadIDs, collection)
22
25
 
23
- /**
24
- * @param { string } loadID
25
- */
26
- export default loadID => {
26
+ export default (/** @type {string} */ loadID) => {
27
27
  const [catalog, setCatalog] = useState(collection.get(loadID))
28
28
  useEffect(() => {
29
29
  callbacks[loadID] = (/** @type {import('wuchale/runtime').CatalogModule} */ catalog) => setCatalog(catalog)
30
+ return () => delete callbacks[loadID]
30
31
  })
31
32
  return catalog
32
33
  }