react-native-jieba 0.1.0 → 0.2.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/README.md CHANGED
@@ -5,6 +5,7 @@ Native Chinese text segmentation for React Native, powered by [cppjieba](https:/
5
5
  - Runs on the New Architecture as a C++ Turbo Module — no bridge overhead, no JS port of jieba.
6
6
  - Ships the cppjieba dictionaries (`jieba.dict.utf8`, `hmm_model.utf8`, `idf.utf8`, `stop_words.utf8`, `user.dict.utf8`) inside the package, bundled as iOS pod resources and Android assets.
7
7
  - Supports the standard jieba modes: precise (`cut`), full (`cutAll`), search engine (`cutForSearch`), HMM (`cutHMM`), small-word (`cutSmall`), POS tagging (`tag`), and TF-IDF keyword extraction (`extract`).
8
+ - Works on **web** (react-native-web) via [`jieba-wasm`](https://github.com/fengkx/jieba-wasm), with the same API.
8
9
 
9
10
  ## Requirements
10
11
 
@@ -28,6 +29,23 @@ cd ios && pod install
28
29
 
29
30
  Android: no extra steps — autolinking picks up the AAR.
30
31
 
32
+ Web (react-native-web): install the optional `jieba-wasm` peer dependency, which provides the WebAssembly backend.
33
+
34
+ ```sh
35
+ npm install jieba-wasm
36
+ ```
37
+
38
+ Any bundler that handles `.wasm` assets (Vite, Webpack 5, Metro-web, etc.) will bundle the binary automatically.
39
+
40
+ If you use **Vite**, exclude `jieba-wasm` from dependency pre-bundling so its `.wasm` is served from the package directory (otherwise the binary isn't copied into `.vite/deps` and the fetch 404s):
41
+
42
+ ```js
43
+ // vite.config.js
44
+ export default {
45
+ optimizeDeps: { exclude: ['jieba-wasm'] },
46
+ };
47
+ ```
48
+
31
49
  ## Usage
32
50
 
33
51
  Call `initJieba()` once before the first segmentation call. On iOS the dictionary path is resolved from the app bundle automatically; on Android it extracts the bundled assets to `filesDir/jieba-dict/` on first run.
@@ -62,11 +80,11 @@ extract('我是拖拉机学院手扶拖拉机专业的。', 5);
62
80
 
63
81
  ## API
64
82
 
65
- All segmentation calls are synchronous JSI calls — no Promises.
83
+ On native, all segmentation calls are synchronous JSI calls — no Promises. On web they are synchronous too, but `initJieba()` must finish first (it loads the wasm module).
66
84
 
67
85
  | Function | Signature | Notes |
68
86
  | --- | --- | --- |
69
- | `initJieba()` | `() => Promise<void>` | Must be awaited once before any other call. Idempotent. |
87
+ | `initJieba(options?)` | `(options?: { wasmUrl?: string \| URL \| Request }) => Promise<void>` | Must be awaited once before any other call. Idempotent. `wasmUrl` is web-only and rarely needed (override where the `.wasm` is served). |
70
88
  | `cut(sentence, hmm?)` | `(string, boolean) => string[]` | Precise mode. `hmm` defaults to `true`. |
71
89
  | `cutAll(sentence)` | `(string) => string[]` | Full mode (every possible word). |
72
90
  | `cutForSearch(sentence, hmm?)` | `(string, boolean) => string[]` | Search-engine mode. |
@@ -77,12 +95,90 @@ All segmentation calls are synchronous JSI calls — no Promises.
77
95
  | `insertUserWord(word, tag?)` | `(string, string) => boolean` | Adds a user dictionary word at runtime. |
78
96
  | `find(word)` | `(string) => boolean` | Tests whether a word is in the dictionary. |
79
97
 
98
+ ### Web support
99
+
100
+ On web the implementation is backed by [`jieba-wasm`](https://github.com/fengkx/jieba-wasm) (jieba-rs compiled to WebAssembly) instead of cppjieba. The core API is identical, with a few caveats because jieba-wasm exposes a smaller surface:
101
+
102
+ | Function | Web behavior |
103
+ | --- | --- |
104
+ | `cut`, `cutAll`, `cutForSearch`, `tag` | Fully supported. |
105
+ | `insertUserWord` | Supported (maps to jieba-wasm `add_word`); the `tag` argument is honored, frequency is auto-assigned. |
106
+ | `cutHMM` | Falls back to `cut(sentence, true)` (jieba-wasm has no HMM-only mode). |
107
+ | `cutSmall` | Falls back to `cut`; the `maxWordLen` argument is accepted for parity but ignored. |
108
+ | `find` | Approximated: returns `true` when the word survives precise (non-HMM) segmentation as a single token. |
109
+ | `extract` | **Not supported** — throws. jieba-wasm does not ship the IDF dictionary needed for TF-IDF extraction. |
110
+
111
+ #### Example (Vite + react-native-web)
112
+
113
+ ```js
114
+ // vite.config.js
115
+ import { defineConfig } from 'vite';
116
+ import react from '@vitejs/plugin-react';
117
+
118
+ export default defineConfig({
119
+ plugins: [react()],
120
+ resolve: {
121
+ alias: { 'react-native': 'react-native-web' },
122
+ },
123
+ // jieba-wasm resolves its `.wasm` via `import.meta.url`; pre-bundling into
124
+ // `.vite/deps` would leave the binary behind, so exclude it.
125
+ optimizeDeps: { exclude: ['jieba-wasm'] },
126
+ });
127
+ ```
128
+
129
+ ```tsx
130
+ // App.tsx
131
+ import { useEffect, useState } from 'react';
132
+ import { Platform, Text, View } from 'react-native';
133
+ import { initJieba, cut, cutForSearch, tag, extract } from 'react-native-jieba';
134
+
135
+ export default function App() {
136
+ const [words, setWords] = useState<string[] | null>(null);
137
+
138
+ useEffect(() => {
139
+ // On web this loads + instantiates the wasm module (async).
140
+ // On native it's a near-instant no-op / dict-path setup.
141
+ initJieba()
142
+ .then(() => {
143
+ setWords(cut('我来到北京清华大学'));
144
+ // → ['我', '来到', '北京', '清华大学']
145
+
146
+ cutForSearch('小明硕士毕业于中国科学院计算所');
147
+ // → ['小明', '硕士', '毕业', '于', '中国', '科学', '学院', ...]
148
+
149
+ tag('我爱北京天安门');
150
+ // → [{ word: '我', tag: 'r' }, { word: '爱', tag: 'v' }, ...]
151
+
152
+ // extract() throws on web — guard it for cross-platform code.
153
+ const keywords = Platform.OS === 'web' ? [] : extract('…', 5);
154
+ })
155
+ .catch((e) => console.error(e));
156
+ }, []);
157
+
158
+ if (!words) return <Text>Loading…</Text>;
159
+ return (
160
+ <View>
161
+ {words.map((w, i) => (
162
+ <Text key={i}>{w}</Text>
163
+ ))}
164
+ </View>
165
+ );
166
+ }
167
+ ```
168
+
169
+ To self-host the binary (CDN or custom path), pass `wasmUrl`:
170
+
171
+ ```ts
172
+ await initJieba({ wasmUrl: 'https://cdn.example.com/jieba_rs_wasm_bg.wasm' });
173
+ ```
174
+
80
175
  ## How it works
81
176
 
82
177
  - The Turbo Module lives in `cpp/JiebaImpl.{h,cpp}` and wraps `cppjieba::Jieba` as a JSI Cxx module.
83
178
  - iOS resolves the dictionary directory from `NSBundle` in `ios/OnLoad.mm` before the module is registered, so `initJieba()` is a no-op on iOS.
84
179
  - Android ships the dictionary as AAR assets and extracts them on demand via `JiebaAndroidHelperModule`, which then hands the path to the C++ module via the codegen-exposed `setDictPath` JSI method.
85
180
  - cppjieba and its `limonp` dependency are vendored as git submodules under `cpp/cppjieba/`. The published npm tarball contains only the headers and dictionaries that are actually needed at build time.
181
+ - On web, `react-native-web`'s bundler resolution picks up `src/NativeJieba.web.ts` and `src/init.web.ts`. `initJieba()` lazily imports `jieba-wasm`, awaits its async wasm initializer, and routes the JS API to the wasm exports.
86
182
 
87
183
  ## Contributing
88
184
 
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ // `jieba-wasm` exposes the same segmentation primitives as cppjieba, but a
4
+ // smaller surface: cut / cutAll / cutForSearch / tag / add_word. The remaining
5
+ // native methods are polyfilled on top of those where the semantics allow, and
6
+ // throw otherwise. The wasm module is loaded lazily by `initJieba()` (see
7
+ // `init.web.ts`) and handed to this shim via `setJiebaWasm`.
8
+
9
+ let wasm = null;
10
+ export function setJiebaWasm(module) {
11
+ wasm = module;
12
+ }
13
+ function getWasm() {
14
+ if (!wasm) {
15
+ throw new Error('react-native-jieba: jieba-wasm is not initialized. ' + 'Call (and await) initJieba() before any segmentation call.');
16
+ }
17
+ return wasm;
18
+ }
19
+ const Jieba = {
20
+ setDictPath() {
21
+ // No-op on web: jieba-wasm bundles its own dictionary.
22
+ },
23
+ cut(sentence, hmm) {
24
+ return getWasm().cut(sentence, hmm);
25
+ },
26
+ cutAll(sentence) {
27
+ return getWasm().cut_all(sentence);
28
+ },
29
+ cutForSearch(sentence, hmm) {
30
+ return getWasm().cut_for_search(sentence, hmm);
31
+ },
32
+ cutHMM(sentence) {
33
+ // jieba-wasm has no HMM-only mode; precise cut with HMM enabled is the
34
+ // closest equivalent.
35
+ return getWasm().cut(sentence, true);
36
+ },
37
+ cutSmall(sentence, _maxWordLen) {
38
+ // jieba-wasm does not expose a max-word-length cut; fall back to precise
39
+ // cut. The maxWordLen argument is accepted for API parity but ignored.
40
+ return getWasm().cut(sentence, true);
41
+ },
42
+ tag(sentence) {
43
+ return getWasm().tag(sentence);
44
+ },
45
+ extract() {
46
+ throw new Error('react-native-jieba: extract() (TF-IDF keyword extraction) is not ' + 'supported on web — jieba-wasm does not ship the IDF dictionary.');
47
+ },
48
+ insertUserWord(word, tag) {
49
+ // add_word returns the inserted word's frequency (>= 0 on success).
50
+ return getWasm().add_word(word, undefined, tag === '' ? undefined : tag) >= 0;
51
+ },
52
+ find(word) {
53
+ // jieba-wasm has no dictionary lookup; approximate it by checking whether
54
+ // the word survives precise (non-HMM) segmentation as a single token.
55
+ const segments = getWasm().cut(word, false);
56
+ return segments.length === 1 && segments[0] === word;
57
+ }
58
+ };
59
+ export default Jieba;
60
+ //# sourceMappingURL=NativeJieba.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["wasm","setJiebaWasm","module","getWasm","Error","Jieba","setDictPath","cut","sentence","hmm","cutAll","cut_all","cutForSearch","cut_for_search","cutHMM","cutSmall","_maxWordLen","tag","extract","insertUserWord","word","add_word","undefined","find","segments","length"],"sourceRoot":"../../src","sources":["NativeJieba.web.ts"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;;AAaA,IAAIA,IAA4B,GAAG,IAAI;AAEvC,OAAO,SAASC,YAAYA,CAACC,MAAuB,EAAQ;EAC1DF,IAAI,GAAGE,MAAM;AACf;AAEA,SAASC,OAAOA,CAAA,EAAoB;EAClC,IAAI,CAACH,IAAI,EAAE;IACT,MAAM,IAAII,KAAK,CACb,qDAAqD,GACnD,4DACJ,CAAC;EACH;EACA,OAAOJ,IAAI;AACb;AAEA,MAAMK,KAAW,GAAG;EAClBC,WAAWA,CAAA,EAAG;IACZ;EAAA,CACD;EACDC,GAAGA,CAACC,QAAQ,EAAEC,GAAG,EAAE;IACjB,OAAON,OAAO,CAAC,CAAC,CAACI,GAAG,CAACC,QAAQ,EAAEC,GAAG,CAAC;EACrC,CAAC;EACDC,MAAMA,CAACF,QAAQ,EAAE;IACf,OAAOL,OAAO,CAAC,CAAC,CAACQ,OAAO,CAACH,QAAQ,CAAC;EACpC,CAAC;EACDI,YAAYA,CAACJ,QAAQ,EAAEC,GAAG,EAAE;IAC1B,OAAON,OAAO,CAAC,CAAC,CAACU,cAAc,CAACL,QAAQ,EAAEC,GAAG,CAAC;EAChD,CAAC;EACDK,MAAMA,CAACN,QAAQ,EAAE;IACf;IACA;IACA,OAAOL,OAAO,CAAC,CAAC,CAACI,GAAG,CAACC,QAAQ,EAAE,IAAI,CAAC;EACtC,CAAC;EACDO,QAAQA,CAACP,QAAQ,EAAEQ,WAAW,EAAE;IAC9B;IACA;IACA,OAAOb,OAAO,CAAC,CAAC,CAACI,GAAG,CAACC,QAAQ,EAAE,IAAI,CAAC;EACtC,CAAC;EACDS,GAAGA,CAACT,QAAQ,EAAE;IACZ,OAAOL,OAAO,CAAC,CAAC,CAACc,GAAG,CAACT,QAAQ,CAAC;EAChC,CAAC;EACDU,OAAOA,CAAA,EAAG;IACR,MAAM,IAAId,KAAK,CACb,mEAAmE,GACjE,iEACJ,CAAC;EACH,CAAC;EACDe,cAAcA,CAACC,IAAI,EAAEH,GAAG,EAAE;IACxB;IACA,OACEd,OAAO,CAAC,CAAC,CAACkB,QAAQ,CAACD,IAAI,EAAEE,SAAS,EAAEL,GAAG,KAAK,EAAE,GAAGK,SAAS,GAAGL,GAAG,CAAC,IAAI,CAAC;EAE1E,CAAC;EACDM,IAAIA,CAACH,IAAI,EAAE;IACT;IACA;IACA,MAAMI,QAAQ,GAAGrB,OAAO,CAAC,CAAC,CAACI,GAAG,CAACa,IAAI,EAAE,KAAK,CAAC;IAC3C,OAAOI,QAAQ,CAACC,MAAM,KAAK,CAAC,IAAID,QAAQ,CAAC,CAAC,CAAC,KAAKJ,IAAI;EACtD;AACF,CAAC;AAED,eAAef,KAAK","ignoreList":[]}
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
 
3
3
  export { cut, cutAll, cutForSearch, cutHMM, cutSmall, tag, extract, insertUserWord, find } from "./jieba.js";
4
- export { initJieba } from "./init.js";
4
+ export { initJieba } from './init';
5
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["cut","cutAll","cutForSearch","cutHMM","cutSmall","tag","extract","insertUserWord","find","initJieba"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SACEA,GAAG,EACHC,MAAM,EACNC,YAAY,EACZC,MAAM,EACNC,QAAQ,EACRC,GAAG,EACHC,OAAO,EACPC,cAAc,EACdC,IAAI,QACC,YAAS;AAEhB,SAASC,SAAS,QAAQ,WAAQ","ignoreList":[]}
1
+ {"version":3,"names":["cut","cutAll","cutForSearch","cutHMM","cutSmall","tag","extract","insertUserWord","find","initJieba"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SACEA,GAAG,EACHC,MAAM,EACNC,YAAY,EACZC,MAAM,EACNC,QAAQ,EACRC,GAAG,EACHC,OAAO,EACPC,cAAc,EACdC,IAAI,QACC,YAAS;AAEhB,SAASC,SAAS,QAAQ,QAAQ","ignoreList":[]}
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  import { Platform } from 'react-native';
4
- import Jieba from "./NativeJieba.js";
4
+ import Jieba from './NativeJieba';
5
5
  import JiebaAndroidHelper from "./NativeJiebaAndroidHelper.js";
6
6
  let initPromise = null;
7
- export function initJieba() {
7
+ export function initJieba(_options = {}) {
8
8
  if (initPromise) return initPromise;
9
9
  initPromise = (async () => {
10
10
  if (Platform.OS === 'android') {
@@ -1 +1 @@
1
- {"version":3,"names":["Platform","Jieba","JiebaAndroidHelper","initPromise","initJieba","OS","Error","path","prepareDictDir","setDictPath","catch","err"],"sourceRoot":"../../src","sources":["init.ts"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,KAAK,MAAM,kBAAe;AACjC,OAAOC,kBAAkB,MAAM,+BAA4B;AAE3D,IAAIC,WAAiC,GAAG,IAAI;AAE5C,OAAO,SAASC,SAASA,CAAA,EAAkB;EACzC,IAAID,WAAW,EAAE,OAAOA,WAAW;EACnCA,WAAW,GAAG,CAAC,YAAY;IACzB,IAAIH,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE;MAC7B,IAAI,CAACH,kBAAkB,EAAE;QACvB,MAAM,IAAII,KAAK,CACb,kEACF,CAAC;MACH;MACA,MAAMC,IAAI,GAAG,MAAML,kBAAkB,CAACM,cAAc,CAAC,CAAC;MACtDP,KAAK,CAACQ,WAAW,CAACF,IAAI,CAAC;IACzB;IACA;EACF,CAAC,EAAE,CAAC,CAACG,KAAK,CAAEC,GAAG,IAAK;IAClBR,WAAW,GAAG,IAAI;IAClB,MAAMQ,GAAG;EACX,CAAC,CAAC;EACF,OAAOR,WAAW;AACpB","ignoreList":[]}
1
+ {"version":3,"names":["Platform","Jieba","JiebaAndroidHelper","initPromise","initJieba","_options","OS","Error","path","prepareDictDir","setDictPath","catch","err"],"sourceRoot":"../../src","sources":["init.ts"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,KAAK,MAAM,eAAe;AACjC,OAAOC,kBAAkB,MAAM,+BAA4B;AAW3D,IAAIC,WAAiC,GAAG,IAAI;AAE5C,OAAO,SAASC,SAASA,CAACC,QAA0B,GAAG,CAAC,CAAC,EAAiB;EACxE,IAAIF,WAAW,EAAE,OAAOA,WAAW;EACnCA,WAAW,GAAG,CAAC,YAAY;IACzB,IAAIH,QAAQ,CAACM,EAAE,KAAK,SAAS,EAAE;MAC7B,IAAI,CAACJ,kBAAkB,EAAE;QACvB,MAAM,IAAIK,KAAK,CACb,kEACF,CAAC;MACH;MACA,MAAMC,IAAI,GAAG,MAAMN,kBAAkB,CAACO,cAAc,CAAC,CAAC;MACtDR,KAAK,CAACS,WAAW,CAACF,IAAI,CAAC;IACzB;IACA;EACF,CAAC,EAAE,CAAC,CAACG,KAAK,CAAEC,GAAG,IAAK;IAClBT,WAAW,GAAG,IAAI;IAClB,MAAMS,GAAG;EACX,CAAC,CAAC;EACF,OAAOT,WAAW;AACpB","ignoreList":[]}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ import { setJiebaWasm } from "./NativeJieba.web.js";
4
+ let initPromise = null;
5
+ export function initJieba(options = {}) {
6
+ if (initPromise) return initPromise;
7
+ initPromise = (async () => {
8
+ // `jieba-wasm` resolves to its `web` build under the `browser`/`import`
9
+ // conditions, whose default export is an async initializer that fetches
10
+ // and instantiates the wasm binary.
11
+ const wasm = await import('jieba-wasm');
12
+
13
+ // With no argument, jieba-wasm's `web` initializer resolves the binary via
14
+ // `new URL('jieba_rs_wasm_bg.wasm', import.meta.url)` *relative to its own
15
+ // module*, which bundlers (Vite, Webpack 5, …) rewrite to the correct
16
+ // emitted asset URL in both dev and production. Only pass `wasmUrl` when the
17
+ // consumer explicitly overrides where the binary is served from — passing a
18
+ // wrong/relative path here makes the dev server return index.html and wasm
19
+ // instantiation fails with a bad magic word.
20
+ await wasm.default(options.wasmUrl);
21
+ setJiebaWasm(wasm);
22
+ })().catch(err => {
23
+ initPromise = null;
24
+ throw err;
25
+ });
26
+ return initPromise;
27
+ }
28
+ //# sourceMappingURL=init.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["setJiebaWasm","initPromise","initJieba","options","wasm","default","wasmUrl","catch","err"],"sourceRoot":"../../src","sources":["init.web.ts"],"mappings":";;AAAA,SAASA,YAAY,QAA8B,sBAAmB;AAKtE,IAAIC,WAAiC,GAAG,IAAI;AAE5C,OAAO,SAASC,SAASA,CAACC,OAAyB,GAAG,CAAC,CAAC,EAAiB;EACvE,IAAIF,WAAW,EAAE,OAAOA,WAAW;EACnCA,WAAW,GAAG,CAAC,YAAY;IACzB;IACA;IACA;IACA,MAAMG,IAAI,GAAI,MAAM,MAAM,CAAC,YAAY,CAEpB;;IAEnB;IACA;IACA;IACA;IACA;IACA;IACA;IACA,MAAMA,IAAI,CAACC,OAAO,CAACF,OAAO,CAACG,OAAO,CAAC;IACnCN,YAAY,CAACI,IAAI,CAAC;EACpB,CAAC,EAAE,CAAC,CAACG,KAAK,CAAEC,GAAG,IAAK;IAClBP,WAAW,GAAG,IAAI;IAClB,MAAMO,GAAG;EACX,CAAC,CAAC;EACF,OAAOP,WAAW;AACpB","ignoreList":[]}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- import Jieba from "./NativeJieba.js";
3
+ import Jieba from './NativeJieba';
4
4
  export function cut(sentence, hmm = true) {
5
5
  return Jieba.cut(sentence, hmm);
6
6
  }
@@ -1 +1 @@
1
- {"version":3,"names":["Jieba","cut","sentence","hmm","cutAll","cutForSearch","cutHMM","cutSmall","maxWordLen","tag","extract","topK","insertUserWord","word","find"],"sourceRoot":"../../src","sources":["jieba.ts"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,kBAAe;AAKjC,OAAO,SAASC,GAAGA,CAACC,QAAgB,EAAEC,GAAY,GAAG,IAAI,EAAY;EACnE,OAAOH,KAAK,CAACC,GAAG,CAACC,QAAQ,EAAEC,GAAG,CAAC;AACjC;AAEA,OAAO,SAASC,MAAMA,CAACF,QAAgB,EAAY;EACjD,OAAOF,KAAK,CAACI,MAAM,CAACF,QAAQ,CAAC;AAC/B;AAEA,OAAO,SAASG,YAAYA,CAC1BH,QAAgB,EAChBC,GAAY,GAAG,IAAI,EACT;EACV,OAAOH,KAAK,CAACK,YAAY,CAACH,QAAQ,EAAEC,GAAG,CAAC;AAC1C;AAEA,OAAO,SAASG,MAAMA,CAACJ,QAAgB,EAAY;EACjD,OAAOF,KAAK,CAACM,MAAM,CAACJ,QAAQ,CAAC;AAC/B;AAEA,OAAO,SAASK,QAAQA,CAACL,QAAgB,EAAEM,UAAkB,EAAY;EACvE,OAAOR,KAAK,CAACO,QAAQ,CAACL,QAAQ,EAAEM,UAAU,CAAC;AAC7C;AAEA,OAAO,SAASC,GAAGA,CAACP,QAAgB,EAAY;EAC9C,OAAOF,KAAK,CAACS,GAAG,CAACP,QAAQ,CAAC;AAC5B;AAEA,OAAO,SAASQ,OAAOA,CAACR,QAAgB,EAAES,IAAY,GAAG,CAAC,EAAa;EACrE,OAAOX,KAAK,CAACU,OAAO,CAACR,QAAQ,EAAES,IAAI,CAAC;AACtC;AAEA,OAAO,SAASC,cAAcA,CAACC,IAAY,EAAEJ,GAAW,GAAG,EAAE,EAAW;EACtE,OAAOT,KAAK,CAACY,cAAc,CAACC,IAAI,EAAEJ,GAAG,CAAC;AACxC;AAEA,OAAO,SAASK,IAAIA,CAACD,IAAY,EAAW;EAC1C,OAAOb,KAAK,CAACc,IAAI,CAACD,IAAI,CAAC;AACzB","ignoreList":[]}
1
+ {"version":3,"names":["Jieba","cut","sentence","hmm","cutAll","cutForSearch","cutHMM","cutSmall","maxWordLen","tag","extract","topK","insertUserWord","word","find"],"sourceRoot":"../../src","sources":["jieba.ts"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,eAAe;AAKjC,OAAO,SAASC,GAAGA,CAACC,QAAgB,EAAEC,GAAY,GAAG,IAAI,EAAY;EACnE,OAAOH,KAAK,CAACC,GAAG,CAACC,QAAQ,EAAEC,GAAG,CAAC;AACjC;AAEA,OAAO,SAASC,MAAMA,CAACF,QAAgB,EAAY;EACjD,OAAOF,KAAK,CAACI,MAAM,CAACF,QAAQ,CAAC;AAC/B;AAEA,OAAO,SAASG,YAAYA,CAACH,QAAgB,EAAEC,GAAY,GAAG,IAAI,EAAY;EAC5E,OAAOH,KAAK,CAACK,YAAY,CAACH,QAAQ,EAAEC,GAAG,CAAC;AAC1C;AAEA,OAAO,SAASG,MAAMA,CAACJ,QAAgB,EAAY;EACjD,OAAOF,KAAK,CAACM,MAAM,CAACJ,QAAQ,CAAC;AAC/B;AAEA,OAAO,SAASK,QAAQA,CAACL,QAAgB,EAAEM,UAAkB,EAAY;EACvE,OAAOR,KAAK,CAACO,QAAQ,CAACL,QAAQ,EAAEM,UAAU,CAAC;AAC7C;AAEA,OAAO,SAASC,GAAGA,CAACP,QAAgB,EAAY;EAC9C,OAAOF,KAAK,CAACS,GAAG,CAACP,QAAQ,CAAC;AAC5B;AAEA,OAAO,SAASQ,OAAOA,CAACR,QAAgB,EAAES,IAAY,GAAG,CAAC,EAAa;EACrE,OAAOX,KAAK,CAACU,OAAO,CAACR,QAAQ,EAAES,IAAI,CAAC;AACtC;AAEA,OAAO,SAASC,cAAcA,CAACC,IAAY,EAAEJ,GAAW,GAAG,EAAE,EAAW;EACtE,OAAOT,KAAK,CAACY,cAAc,CAACC,IAAI,EAAEJ,GAAG,CAAC;AACxC;AAEA,OAAO,SAASK,IAAIA,CAACD,IAAY,EAAW;EAC1C,OAAOb,KAAK,CAACc,IAAI,CAACD,IAAI,CAAC;AACzB","ignoreList":[]}
@@ -0,0 +1,15 @@
1
+ import type { Spec } from './NativeJieba';
2
+ export type JiebaWasmModule = {
3
+ cut(text: string, hmm?: boolean | null): string[];
4
+ cut_all(text: string): string[];
5
+ cut_for_search(text: string, hmm?: boolean | null): string[];
6
+ tag(sentence: string, hmm?: boolean | null): Array<{
7
+ word: string;
8
+ tag: string;
9
+ }>;
10
+ add_word(word: string, freq?: number | null, tag?: string | null): number;
11
+ };
12
+ export declare function setJiebaWasm(module: JiebaWasmModule): void;
13
+ declare const Jieba: Spec;
14
+ export default Jieba;
15
+ //# sourceMappingURL=NativeJieba.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeJieba.web.d.ts","sourceRoot":"","sources":["../../../src/NativeJieba.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAQ1C,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,EAAE,CAAC;IAClD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAChC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,EAAE,CAAC;IAC7D,GAAG,CACD,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,GACnB,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;CAC3E,CAAC;AAIF,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAE1D;AAYD,QAAA,MAAM,KAAK,EAAE,IA4CZ,CAAC;AAEF,eAAe,KAAK,CAAC"}
@@ -1,2 +1,10 @@
1
- export declare function initJieba(): Promise<void>;
1
+ export type InitJiebaOptions = {
2
+ /**
3
+ * Web only: URL (or fetch input) for the jieba-wasm `.wasm` binary. Most
4
+ * bundlers resolve it automatically, so this is only needed when the asset
5
+ * is served from a custom location. Ignored on iOS and Android.
6
+ */
7
+ wasmUrl?: string | URL | Request;
8
+ };
9
+ export declare function initJieba(_options?: InitJiebaOptions): Promise<void>;
2
10
  //# sourceMappingURL=init.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/init.ts"],"names":[],"mappings":"AAMA,wBAAgB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAkBzC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/init.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC;CAClC,CAAC;AAIF,wBAAgB,SAAS,CAAC,QAAQ,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBxE"}
@@ -0,0 +1,4 @@
1
+ import type { InitJiebaOptions } from './init';
2
+ export type { InitJiebaOptions } from './init';
3
+ export declare function initJieba(options?: InitJiebaOptions): Promise<void>;
4
+ //# sourceMappingURL=init.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.web.d.ts","sourceRoot":"","sources":["../../../src/init.web.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAE/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAI/C,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"jieba.d.ts","sourceRoot":"","sources":["../../../src/jieba.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AACnD,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,wBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,OAAc,GAAG,MAAM,EAAE,CAEnE;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjD;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,OAAc,GAClB,MAAM,EAAE,CAEV;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjD;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvE;AAED,wBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9C;AAED,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,EAAE,CAErE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAW,GAAG,OAAO,CAEtE;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1C"}
1
+ {"version":3,"file":"jieba.d.ts","sourceRoot":"","sources":["../../../src/jieba.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AACnD,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,wBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,OAAc,GAAG,MAAM,EAAE,CAEnE;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,OAAc,GAAG,MAAM,EAAE,CAE5E;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjD;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvE;AAED,wBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9C;AAED,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,EAAE,CAErE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAW,GAAG,OAAO,CAEtE;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-jieba",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Native Chinese text segmentation for React Native, powered by cppjieba.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -94,6 +94,7 @@
94
94
  "eslint-plugin-ft-flow": "^3.0.11",
95
95
  "eslint-plugin-prettier": "^5.5.5",
96
96
  "jest": "^30.3.0",
97
+ "jieba-wasm": "^2.4.0",
97
98
  "lefthook": "^2.1.4",
98
99
  "prettier": "^3.8.1",
99
100
  "react": "19.2.3",
@@ -105,9 +106,15 @@
105
106
  "typescript": "^6.0.2"
106
107
  },
107
108
  "peerDependencies": {
109
+ "jieba-wasm": ">=2.0.0",
108
110
  "react": ">=18.0.0",
109
111
  "react-native": ">=0.85.0"
110
112
  },
113
+ "peerDependenciesMeta": {
114
+ "jieba-wasm": {
115
+ "optional": true
116
+ }
117
+ },
111
118
  "workspaces": [
112
119
  "example"
113
120
  ],
@@ -0,0 +1,82 @@
1
+ import type { Spec } from './NativeJieba';
2
+
3
+ // `jieba-wasm` exposes the same segmentation primitives as cppjieba, but a
4
+ // smaller surface: cut / cutAll / cutForSearch / tag / add_word. The remaining
5
+ // native methods are polyfilled on top of those where the semantics allow, and
6
+ // throw otherwise. The wasm module is loaded lazily by `initJieba()` (see
7
+ // `init.web.ts`) and handed to this shim via `setJiebaWasm`.
8
+
9
+ export type JiebaWasmModule = {
10
+ cut(text: string, hmm?: boolean | null): string[];
11
+ cut_all(text: string): string[];
12
+ cut_for_search(text: string, hmm?: boolean | null): string[];
13
+ tag(
14
+ sentence: string,
15
+ hmm?: boolean | null
16
+ ): Array<{ word: string; tag: string }>;
17
+ add_word(word: string, freq?: number | null, tag?: string | null): number;
18
+ };
19
+
20
+ let wasm: JiebaWasmModule | null = null;
21
+
22
+ export function setJiebaWasm(module: JiebaWasmModule): void {
23
+ wasm = module;
24
+ }
25
+
26
+ function getWasm(): JiebaWasmModule {
27
+ if (!wasm) {
28
+ throw new Error(
29
+ 'react-native-jieba: jieba-wasm is not initialized. ' +
30
+ 'Call (and await) initJieba() before any segmentation call.'
31
+ );
32
+ }
33
+ return wasm;
34
+ }
35
+
36
+ const Jieba: Spec = {
37
+ setDictPath() {
38
+ // No-op on web: jieba-wasm bundles its own dictionary.
39
+ },
40
+ cut(sentence, hmm) {
41
+ return getWasm().cut(sentence, hmm);
42
+ },
43
+ cutAll(sentence) {
44
+ return getWasm().cut_all(sentence);
45
+ },
46
+ cutForSearch(sentence, hmm) {
47
+ return getWasm().cut_for_search(sentence, hmm);
48
+ },
49
+ cutHMM(sentence) {
50
+ // jieba-wasm has no HMM-only mode; precise cut with HMM enabled is the
51
+ // closest equivalent.
52
+ return getWasm().cut(sentence, true);
53
+ },
54
+ cutSmall(sentence, _maxWordLen) {
55
+ // jieba-wasm does not expose a max-word-length cut; fall back to precise
56
+ // cut. The maxWordLen argument is accepted for API parity but ignored.
57
+ return getWasm().cut(sentence, true);
58
+ },
59
+ tag(sentence) {
60
+ return getWasm().tag(sentence);
61
+ },
62
+ extract() {
63
+ throw new Error(
64
+ 'react-native-jieba: extract() (TF-IDF keyword extraction) is not ' +
65
+ 'supported on web — jieba-wasm does not ship the IDF dictionary.'
66
+ );
67
+ },
68
+ insertUserWord(word, tag) {
69
+ // add_word returns the inserted word's frequency (>= 0 on success).
70
+ return (
71
+ getWasm().add_word(word, undefined, tag === '' ? undefined : tag) >= 0
72
+ );
73
+ },
74
+ find(word) {
75
+ // jieba-wasm has no dictionary lookup; approximate it by checking whether
76
+ // the word survives precise (non-HMM) segmentation as a single token.
77
+ const segments = getWasm().cut(word, false);
78
+ return segments.length === 1 && segments[0] === word;
79
+ },
80
+ };
81
+
82
+ export default Jieba;
package/src/init.ts CHANGED
@@ -2,9 +2,18 @@ import { Platform } from 'react-native';
2
2
  import Jieba from './NativeJieba';
3
3
  import JiebaAndroidHelper from './NativeJiebaAndroidHelper';
4
4
 
5
+ export type InitJiebaOptions = {
6
+ /**
7
+ * Web only: URL (or fetch input) for the jieba-wasm `.wasm` binary. Most
8
+ * bundlers resolve it automatically, so this is only needed when the asset
9
+ * is served from a custom location. Ignored on iOS and Android.
10
+ */
11
+ wasmUrl?: string | URL | Request;
12
+ };
13
+
5
14
  let initPromise: Promise<void> | null = null;
6
15
 
7
- export function initJieba(): Promise<void> {
16
+ export function initJieba(_options: InitJiebaOptions = {}): Promise<void> {
8
17
  if (initPromise) return initPromise;
9
18
  initPromise = (async () => {
10
19
  if (Platform.OS === 'android') {
@@ -0,0 +1,32 @@
1
+ import { setJiebaWasm, type JiebaWasmModule } from './NativeJieba.web';
2
+ import type { InitJiebaOptions } from './init';
3
+
4
+ export type { InitJiebaOptions } from './init';
5
+
6
+ let initPromise: Promise<void> | null = null;
7
+
8
+ export function initJieba(options: InitJiebaOptions = {}): Promise<void> {
9
+ if (initPromise) return initPromise;
10
+ initPromise = (async () => {
11
+ // `jieba-wasm` resolves to its `web` build under the `browser`/`import`
12
+ // conditions, whose default export is an async initializer that fetches
13
+ // and instantiates the wasm binary.
14
+ const wasm = (await import('jieba-wasm')) as unknown as {
15
+ default: (input?: string | URL | Request) => Promise<unknown>;
16
+ } & JiebaWasmModule;
17
+
18
+ // With no argument, jieba-wasm's `web` initializer resolves the binary via
19
+ // `new URL('jieba_rs_wasm_bg.wasm', import.meta.url)` *relative to its own
20
+ // module*, which bundlers (Vite, Webpack 5, …) rewrite to the correct
21
+ // emitted asset URL in both dev and production. Only pass `wasmUrl` when the
22
+ // consumer explicitly overrides where the binary is served from — passing a
23
+ // wrong/relative path here makes the dev server return index.html and wasm
24
+ // instantiation fails with a bad magic word.
25
+ await wasm.default(options.wasmUrl);
26
+ setJiebaWasm(wasm);
27
+ })().catch((err) => {
28
+ initPromise = null;
29
+ throw err;
30
+ });
31
+ return initPromise;
32
+ }
package/src/jieba.ts CHANGED
@@ -11,10 +11,7 @@ export function cutAll(sentence: string): string[] {
11
11
  return Jieba.cutAll(sentence);
12
12
  }
13
13
 
14
- export function cutForSearch(
15
- sentence: string,
16
- hmm: boolean = true
17
- ): string[] {
14
+ export function cutForSearch(sentence: string, hmm: boolean = true): string[] {
18
15
  return Jieba.cutForSearch(sentence, hmm);
19
16
  }
20
17