react-native-jieba 0.3.0 → 0.4.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/Jieba.podspec +15 -1
- package/README.md +42 -3
- package/android/CMakeLists.txt +1 -0
- package/android/build.gradle +32 -0
- package/android/src/main/java/com/jieba/JiebaDict.kt +17 -7
- package/cpp/JiebaEngine.cpp +18 -0
- package/cpp/JiebaEngine.h +128 -0
- package/cpp/JiebaImpl.cpp +8 -6
- package/cpp/JiebaImpl.h +3 -3
- package/lib/module/jieba.js +12 -0
- package/lib/module/jieba.js.map +1 -1
- package/lib/typescript/src/jieba.d.ts +11 -0
- package/lib/typescript/src/jieba.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/jieba.ts +11 -0
package/Jieba.podspec
CHANGED
|
@@ -18,7 +18,21 @@ Pod::Spec.new do |s|
|
|
|
18
18
|
s.exclude_files = "cpp/JiebaDictAndroid.{cpp,h}"
|
|
19
19
|
s.private_header_files = "ios/**/*.h"
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
# The IDF dictionary (~5MB) is only read by `extract()` (TF-IDF keyword extraction). Apps that
|
|
22
|
+
# only tokenize can drop it from the bundle by setting RN_JIEBA_EXCLUDE_IDF_DICT=1 in the
|
|
23
|
+
# environment that evaluates the Podfile, e.g.
|
|
24
|
+
#
|
|
25
|
+
# ENV["RN_JIEBA_EXCLUDE_IDF_DICT"] = "1" # in the Podfile, before use_react_native!
|
|
26
|
+
#
|
|
27
|
+
# `extract()` then throws a descriptive error, exactly as it already does on web (jieba-wasm
|
|
28
|
+
# ships no IDF data). Every other API is unaffected.
|
|
29
|
+
exclude_idf_dict = ENV["RN_JIEBA_EXCLUDE_IDF_DICT"] == "1"
|
|
30
|
+
dict_files = Dir.glob(File.join(__dir__, "cpp/cppjieba/dict/*.utf8")).map do |path|
|
|
31
|
+
"cpp/cppjieba/dict/#{File.basename(path)}"
|
|
32
|
+
end
|
|
33
|
+
dict_files.reject! { |path| File.basename(path) == "idf.utf8" } if exclude_idf_dict
|
|
34
|
+
|
|
35
|
+
s.resources = dict_files
|
|
22
36
|
|
|
23
37
|
s.pod_target_xcconfig = {
|
|
24
38
|
"HEADER_SEARCH_PATHS" => [
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Native Chinese text segmentation for React Native, powered by [cppjieba](https://github.com/yanyiwu/cppjieba).
|
|
4
4
|
|
|
5
5
|
- Runs on the New Architecture as a C++ Turbo Module — no bridge overhead, no JS port of jieba.
|
|
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.
|
|
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. The ~5.7 MB `idf.utf8` is loaded lazily and [can be excluded entirely](#reducing-app-size-excluding-the-idf-dictionary) in apps that never call `extract()`.
|
|
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
8
|
- Works on **web** (react-native-web) via [`jieba-wasm`](https://github.com/fengkx/jieba-wasm), with the same API.
|
|
9
9
|
|
|
@@ -105,7 +105,7 @@ On native, all segmentation calls are synchronous JSI calls — no Promises. On
|
|
|
105
105
|
| `cutHMM(sentence)` | `(string) => string[]` | HMM-only segmentation. |
|
|
106
106
|
| `cutSmall(sentence, maxWordLen)` | `(string, number) => string[]` | Limits the maximum word length. |
|
|
107
107
|
| `tag(sentence)` | `(string) => Array<{ word, tag }>` | Part-of-speech tagging. |
|
|
108
|
-
| `extract(sentence, topK?)` | `(string, number) => Array<{ word, weight }>` | TF-IDF keyword extraction. `topK` defaults to `5`. |
|
|
108
|
+
| `extract(sentence, topK?)` | `(string, number) => Array<{ word, weight }>` | TF-IDF keyword extraction. `topK` defaults to `5`. Loads the IDF dictionary on first use; throws on web, and in native builds that [excluded it](#reducing-app-size-excluding-the-idf-dictionary). |
|
|
109
109
|
| `insertUserWord(word, tag?)` | `(string, string) => boolean` | Adds a user dictionary word at runtime. |
|
|
110
110
|
| `find(word)` | `(string) => boolean` | Tests whether a word is in the dictionary. |
|
|
111
111
|
|
|
@@ -186,9 +186,48 @@ To self-host the binary (CDN or custom path), pass `wasmUrl`:
|
|
|
186
186
|
await prepareJieba({ wasmUrl: 'https://cdn.example.com/jieba_rs_wasm_bg.wasm' });
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
+
## Reducing app size: excluding the IDF dictionary
|
|
190
|
+
|
|
191
|
+
The bundled dictionaries account for most of this library's footprint, and `idf.utf8` is the
|
|
192
|
+
largest single file at **~5.7 MB**. It is read by exactly one API — `extract()`, TF-IDF keyword
|
|
193
|
+
extraction. Apps that only segment text (`cut`, `cutForSearch`, `tag`, …) never touch it.
|
|
194
|
+
|
|
195
|
+
The extractor is built lazily on first use, so the dictionary costs nothing at startup either way.
|
|
196
|
+
If your app never calls `extract()`, you can also drop the file from your binary entirely:
|
|
197
|
+
|
|
198
|
+
**iOS** — set the environment variable before CocoaPods evaluates the podspec, e.g. at the top of
|
|
199
|
+
your `Podfile`:
|
|
200
|
+
|
|
201
|
+
```rb
|
|
202
|
+
ENV['RN_JIEBA_EXCLUDE_IDF_DICT'] = '1'
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Then reinstall pods (`bundle exec pod install`).
|
|
206
|
+
|
|
207
|
+
**Android** — set a Gradle property in `android/gradle.properties`:
|
|
208
|
+
|
|
209
|
+
```properties
|
|
210
|
+
rnJiebaExcludeIdfDict=true
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
(or `ext.rnJiebaExcludeIdfDict = true` in `android/build.gradle`, or pass
|
|
214
|
+
`-PrnJiebaExcludeIdfDict=true` on the command line).
|
|
215
|
+
|
|
216
|
+
With the dictionary excluded, `extract()` throws a descriptive error and **every other API keeps
|
|
217
|
+
working unchanged** — the same contract that already applies on web, where `extract()` is
|
|
218
|
+
unsupported because jieba-wasm ships no IDF data. Guard it as you would for web:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const keywords = canExtract ? extract(sentence, 5) : [];
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
On Android this also skips copying the file out of the APK into `filesDir/jieba-dict/` on first
|
|
225
|
+
run, saving the same ~5.7 MB of user device storage and shortening first-call extraction.
|
|
226
|
+
|
|
189
227
|
## How it works
|
|
190
228
|
|
|
191
|
-
- The Turbo Module lives in `cpp/JiebaImpl.{h,cpp}` and
|
|
229
|
+
- The Turbo Module lives in `cpp/JiebaImpl.{h,cpp}` and exposes the engine as a JSI Cxx module.
|
|
230
|
+
- `cpp/JiebaEngine.{h,cpp}` composes cppjieba's primitives (one shared `DictTrie` + `HMMModel` across every segmenter) instead of using the `cppjieba::Jieba` facade. The facade holds a `KeywordExtractor` **by value**, so constructing it always loads `idf.utf8` and hard-fails when that file is absent; composing the pieces directly is what lets the extractor — and its dictionary — be built only if `extract()` is called.
|
|
192
231
|
- iOS resolves the dictionary directory from `NSBundle` in `ios/OnLoad.mm` before the module is registered, so segmentation works immediately and `prepareJieba()` is a no-op on iOS.
|
|
193
232
|
- Android ships the dictionary as AAR assets and extracts them to `filesDir/jieba-dict/`. This happens automatically: if `prepareJieba()` is never called, the C++ module extracts them synchronously on the first segmentation call via an fbjni call into `JiebaDict.extractDictDirFromNative` (a one-time cost). `prepareJieba()` does the same extraction asynchronously up front (through `JiebaAndroidHelperModule` → the codegen-exposed `setDictPath` JSI method) so that first call doesn't block.
|
|
194
233
|
- `isJiebaReady()` is backed by the codegen-exposed `isReady()` JSI method, which reads the native engine state directly — so it stays correct even when the dictionary is resolved lazily on the first call.
|
package/android/CMakeLists.txt
CHANGED
package/android/build.gradle
CHANGED
|
@@ -2,6 +2,14 @@ buildscript {
|
|
|
2
2
|
ext.safeExtGet = { prop, fallback ->
|
|
3
3
|
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
4
4
|
}
|
|
5
|
+
// Resolves a flag from either `ext` (…/build.gradle: ext.rnJiebaExcludeIdfDict = true) or a
|
|
6
|
+
// Gradle property (gradle.properties: rnJiebaExcludeIdfDict=true / -PrnJiebaExcludeIdfDict=true),
|
|
7
|
+
// so consumers can set it whichever way their project already configures native modules.
|
|
8
|
+
ext.safeFlagGet = { prop, fallback ->
|
|
9
|
+
if (rootProject.ext.has(prop)) return rootProject.ext.get(prop).toString()
|
|
10
|
+
if (rootProject.hasProperty(prop)) return rootProject.property(prop).toString()
|
|
11
|
+
return fallback
|
|
12
|
+
}
|
|
5
13
|
repositories {
|
|
6
14
|
google()
|
|
7
15
|
mavenCentral()
|
|
@@ -24,6 +32,11 @@ android {
|
|
|
24
32
|
defaultConfig {
|
|
25
33
|
minSdk safeExtGet('minSdkVersion', 24)
|
|
26
34
|
targetSdk safeExtGet('targetSdkVersion', 35)
|
|
35
|
+
buildConfigField(
|
|
36
|
+
"boolean",
|
|
37
|
+
"RN_JIEBA_EXCLUDE_IDF_DICT",
|
|
38
|
+
safeFlagGet('rnJiebaExcludeIdfDict', 'false')
|
|
39
|
+
)
|
|
27
40
|
externalNativeBuild {
|
|
28
41
|
cmake {
|
|
29
42
|
cppFlags "-std=c++20"
|
|
@@ -53,6 +66,25 @@ android {
|
|
|
53
66
|
assets.srcDirs += "$projectDir/../cpp/cppjieba/dict"
|
|
54
67
|
}
|
|
55
68
|
}
|
|
69
|
+
|
|
70
|
+
// The IDF dictionary (~5MB) is only read by `extract()` (TF-IDF keyword extraction). Apps that
|
|
71
|
+
// only tokenize can drop it from the APK/AAB with:
|
|
72
|
+
//
|
|
73
|
+
// rnJiebaExcludeIdfDict=true # in android/gradle.properties, or
|
|
74
|
+
// ext.rnJiebaExcludeIdfDict = true # in android/build.gradle
|
|
75
|
+
//
|
|
76
|
+
// `extract()` then throws a descriptive error, exactly as it already does on web (jieba-wasm
|
|
77
|
+
// ships no IDF data). Every other API is unaffected. BuildConfig carries the same flag through to
|
|
78
|
+
// JiebaDict so the extractor does not try to copy a file that was never packaged.
|
|
79
|
+
buildFeatures {
|
|
80
|
+
buildConfig true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (safeFlagGet('rnJiebaExcludeIdfDict', 'false') == 'true') {
|
|
84
|
+
androidResources {
|
|
85
|
+
ignoreAssetsPattern "idf.utf8"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
56
88
|
}
|
|
57
89
|
|
|
58
90
|
dependencies {
|
|
@@ -21,13 +21,23 @@ import java.io.FileOutputStream
|
|
|
21
21
|
object JiebaDict {
|
|
22
22
|
const val DICT_DIR_NAME = "jieba-dict"
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
/** The IDF dictionary, needed only by `extract()` (TF-IDF keyword extraction). */
|
|
25
|
+
const val IDF_DICT_FILE = "idf.utf8"
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Dictionary assets to extract.
|
|
29
|
+
*
|
|
30
|
+
* [IDF_DICT_FILE] is omitted when the library was built with `rnJiebaExcludeIdfDict=true`, since
|
|
31
|
+
* the asset is then not packaged at all and opening it would throw. `extract()` reports the
|
|
32
|
+
* omission with a descriptive error; every other API is unaffected.
|
|
33
|
+
*/
|
|
34
|
+
val DICT_FILES: Array<String> = buildList {
|
|
35
|
+
add("jieba.dict.utf8")
|
|
36
|
+
add("hmm_model.utf8")
|
|
37
|
+
add("user.dict.utf8")
|
|
38
|
+
if (!BuildConfig.RN_JIEBA_EXCLUDE_IDF_DICT) add(IDF_DICT_FILE)
|
|
39
|
+
add("stop_words.utf8")
|
|
40
|
+
}.toTypedArray()
|
|
31
41
|
|
|
32
42
|
/**
|
|
33
43
|
* Application context captured at module construction so the native (C++)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#include "JiebaEngine.h"
|
|
2
|
+
|
|
3
|
+
#include <fstream>
|
|
4
|
+
|
|
5
|
+
namespace rnjieba {
|
|
6
|
+
|
|
7
|
+
bool JiebaEngine::UnicodeFileExists(const std::string& path) {
|
|
8
|
+
if (path.empty()) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
// Probe by opening rather than stat()-ing: cppjieba itself loads the dictionary through
|
|
12
|
+
// an ifstream, so this reports exactly the condition that would otherwise trip its
|
|
13
|
+
// XCHECK — including a path that exists but cannot be read.
|
|
14
|
+
std::ifstream ifs(path.c_str());
|
|
15
|
+
return ifs.is_open();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cppjieba/DictTrie.hpp>
|
|
4
|
+
#include <cppjieba/FullSegment.hpp>
|
|
5
|
+
#include <cppjieba/HMMModel.hpp>
|
|
6
|
+
#include <cppjieba/HMMSegment.hpp>
|
|
7
|
+
#include <cppjieba/KeywordExtractor.hpp>
|
|
8
|
+
#include <cppjieba/MPSegment.hpp>
|
|
9
|
+
#include <cppjieba/MixSegment.hpp>
|
|
10
|
+
#include <cppjieba/QuerySegment.hpp>
|
|
11
|
+
|
|
12
|
+
#include <memory>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <utility>
|
|
15
|
+
#include <vector>
|
|
16
|
+
|
|
17
|
+
namespace rnjieba {
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A cppjieba engine whose keyword extractor is built ON DEMAND.
|
|
21
|
+
*
|
|
22
|
+
* `cppjieba::Jieba` holds `KeywordExtractor` by value, so its constructor always
|
|
23
|
+
* loads `idf.utf8` and hard-fails (XCHECK) when that file is absent. That makes the
|
|
24
|
+
* ~5MB IDF dictionary a mandatory dependency of plain segmentation, even though only
|
|
25
|
+
* `extract()` ever reads it.
|
|
26
|
+
*
|
|
27
|
+
* This type composes the same cppjieba primitives directly, sharing one `DictTrie` and
|
|
28
|
+
* one `HMMModel` across every segmenter exactly as `cppjieba::Jieba` does, but defers the
|
|
29
|
+
* `KeywordExtractor` until `extract()` is first called. Apps that only tokenize therefore
|
|
30
|
+
* never load the IDF dictionary — and may omit the file from their bundle entirely (see
|
|
31
|
+
* `excludeIdfDict` in the README).
|
|
32
|
+
*/
|
|
33
|
+
class JiebaEngine {
|
|
34
|
+
public:
|
|
35
|
+
JiebaEngine(
|
|
36
|
+
std::string dictPath,
|
|
37
|
+
std::string hmmModelPath,
|
|
38
|
+
std::string userDictPath,
|
|
39
|
+
std::string idfPath,
|
|
40
|
+
std::string stopWordPath
|
|
41
|
+
)
|
|
42
|
+
: idfPath_(std::move(idfPath)),
|
|
43
|
+
stopWordPath_(std::move(stopWordPath)),
|
|
44
|
+
dictTrie_(dictPath, userDictPath),
|
|
45
|
+
model_(hmmModelPath),
|
|
46
|
+
mpSeg_(&dictTrie_),
|
|
47
|
+
hmmSeg_(&model_),
|
|
48
|
+
mixSeg_(&dictTrie_, &model_),
|
|
49
|
+
fullSeg_(&dictTrie_),
|
|
50
|
+
querySeg_(&dictTrie_, &model_) {}
|
|
51
|
+
|
|
52
|
+
void Cut(const std::string& sentence, std::vector<std::string>& words, bool hmm) const {
|
|
53
|
+
mixSeg_.Cut(sentence, words, hmm);
|
|
54
|
+
}
|
|
55
|
+
void CutAll(const std::string& sentence, std::vector<std::string>& words) const {
|
|
56
|
+
fullSeg_.Cut(sentence, words);
|
|
57
|
+
}
|
|
58
|
+
void CutForSearch(const std::string& sentence, std::vector<std::string>& words, bool hmm) const {
|
|
59
|
+
querySeg_.Cut(sentence, words, hmm);
|
|
60
|
+
}
|
|
61
|
+
void CutHMM(const std::string& sentence, std::vector<std::string>& words) const {
|
|
62
|
+
hmmSeg_.Cut(sentence, words);
|
|
63
|
+
}
|
|
64
|
+
void CutSmall(
|
|
65
|
+
const std::string& sentence,
|
|
66
|
+
std::vector<std::string>& words,
|
|
67
|
+
size_t maxWordLen
|
|
68
|
+
) const {
|
|
69
|
+
mpSeg_.Cut(sentence, words, maxWordLen);
|
|
70
|
+
}
|
|
71
|
+
void Tag(
|
|
72
|
+
const std::string& sentence,
|
|
73
|
+
std::vector<std::pair<std::string, std::string>>& words
|
|
74
|
+
) const {
|
|
75
|
+
mixSeg_.Tag(sentence, words);
|
|
76
|
+
}
|
|
77
|
+
bool InsertUserWord(const std::string& word, const std::string& tag) {
|
|
78
|
+
return dictTrie_.InsertUserWord(word, tag);
|
|
79
|
+
}
|
|
80
|
+
bool Find(const std::string& word) {
|
|
81
|
+
return dictTrie_.Find(word);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The TF-IDF keyword extractor, constructed on first use.
|
|
86
|
+
*
|
|
87
|
+
* Reuses this engine's already-loaded trie and HMM model, so the only additional cost is
|
|
88
|
+
* reading `idf.utf8` and `stop_words.utf8`. Throws `std::runtime_error` when the IDF
|
|
89
|
+
* dictionary is missing — mirroring the web backend, where `extract()` is likewise
|
|
90
|
+
* unsupported because jieba-wasm ships no IDF data.
|
|
91
|
+
*/
|
|
92
|
+
const cppjieba::KeywordExtractor& Extractor() {
|
|
93
|
+
if (!extractor_) {
|
|
94
|
+
if (!UnicodeFileExists(idfPath_)) {
|
|
95
|
+
throw std::runtime_error(
|
|
96
|
+
"react-native-jieba: extract() is unavailable because the IDF dictionary was not "
|
|
97
|
+
"found at '" + idfPath_ + "'. This build excluded it (see the `excludeIdfDict` "
|
|
98
|
+
"build flag). Re-enable the IDF dictionary to use extract(); cut()/tag() are "
|
|
99
|
+
"unaffected."
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
extractor_ = std::make_unique<cppjieba::KeywordExtractor>(
|
|
103
|
+
&dictTrie_, &model_, idfPath_, stopWordPath_
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return *extractor_;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private:
|
|
110
|
+
static bool UnicodeFileExists(const std::string& path);
|
|
111
|
+
|
|
112
|
+
std::string idfPath_;
|
|
113
|
+
std::string stopWordPath_;
|
|
114
|
+
|
|
115
|
+
cppjieba::DictTrie dictTrie_;
|
|
116
|
+
cppjieba::HMMModel model_;
|
|
117
|
+
|
|
118
|
+
// All share the trie and model above.
|
|
119
|
+
cppjieba::MPSegment mpSeg_;
|
|
120
|
+
cppjieba::HMMSegment hmmSeg_;
|
|
121
|
+
cppjieba::MixSegment mixSeg_;
|
|
122
|
+
cppjieba::FullSegment fullSeg_;
|
|
123
|
+
cppjieba::QuerySegment querySeg_;
|
|
124
|
+
|
|
125
|
+
std::unique_ptr<cppjieba::KeywordExtractor> extractor_;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
}
|
package/cpp/JiebaImpl.cpp
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
#include "JiebaDictAndroid.h"
|
|
5
5
|
#endif
|
|
6
6
|
|
|
7
|
-
#include
|
|
7
|
+
#include "JiebaEngine.h"
|
|
8
8
|
|
|
9
9
|
#include <memory>
|
|
10
10
|
#include <mutex>
|
|
@@ -27,8 +27,8 @@ std::string& dictPathStorage() {
|
|
|
27
27
|
return p;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
std::unique_ptr<
|
|
31
|
-
static std::unique_ptr<
|
|
30
|
+
std::unique_ptr<rnjieba::JiebaEngine>& jiebaStorage() {
|
|
31
|
+
static std::unique_ptr<rnjieba::JiebaEngine> j;
|
|
32
32
|
return j;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -74,7 +74,7 @@ void JiebaImpl::setDictPath(jsi::Runtime& rt, jsi::String path) {
|
|
|
74
74
|
setDictPathFromNative(path.utf8(rt));
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
rnjieba::JiebaEngine& JiebaImpl::getJieba() {
|
|
78
78
|
std::lock_guard<std::mutex> lock(dictMutex());
|
|
79
79
|
if (!jiebaStorage()) {
|
|
80
80
|
#ifdef __ANDROID__
|
|
@@ -95,7 +95,9 @@ cppjieba::Jieba& JiebaImpl::getJieba() {
|
|
|
95
95
|
"The native module failed to locate bundled dict files."
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
|
-
|
|
98
|
+
// The IDF path is recorded but NOT read here: JiebaEngine loads it only if extract() is
|
|
99
|
+
// called, so a build that omits idf.utf8 still tokenizes normally.
|
|
100
|
+
jiebaStorage() = std::make_unique<rnjieba::JiebaEngine>(
|
|
99
101
|
joinPath(base, "jieba.dict.utf8"),
|
|
100
102
|
joinPath(base, "hmm_model.utf8"),
|
|
101
103
|
joinPath(base, "user.dict.utf8"),
|
|
@@ -153,7 +155,7 @@ jsi::Array JiebaImpl::tag(jsi::Runtime& rt, jsi::String sentence) {
|
|
|
153
155
|
jsi::Array JiebaImpl::extract(jsi::Runtime& rt, jsi::String sentence, double topK) {
|
|
154
156
|
std::vector<cppjieba::KeywordExtractor::Word> keywords;
|
|
155
157
|
size_t n = topK <= 0 ? 5 : static_cast<size_t>(topK);
|
|
156
|
-
getJieba().
|
|
158
|
+
getJieba().Extractor().Extract(sentence.utf8(rt), keywords, n);
|
|
157
159
|
jsi::Array arr(rt, keywords.size());
|
|
158
160
|
for (size_t i = 0; i < keywords.size(); ++i) {
|
|
159
161
|
jsi::Object obj(rt);
|
package/cpp/JiebaImpl.h
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
#include <mutex>
|
|
7
7
|
#include <string>
|
|
8
8
|
|
|
9
|
-
namespace
|
|
10
|
-
class
|
|
9
|
+
namespace rnjieba {
|
|
10
|
+
class JiebaEngine;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
namespace facebook::react {
|
|
@@ -32,7 +32,7 @@ public:
|
|
|
32
32
|
bool find(jsi::Runtime& rt, jsi::String word);
|
|
33
33
|
|
|
34
34
|
private:
|
|
35
|
-
|
|
35
|
+
rnjieba::JiebaEngine& getJieba();
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
}
|
package/lib/module/jieba.js
CHANGED
|
@@ -19,6 +19,18 @@ export function cutSmall(sentence, maxWordLen) {
|
|
|
19
19
|
export function tag(sentence) {
|
|
20
20
|
return Jieba.tag(sentence);
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* TF-IDF keyword extraction.
|
|
25
|
+
*
|
|
26
|
+
* Requires the IDF dictionary, which is loaded lazily on the first call (every other API works
|
|
27
|
+
* without it). Throws when that dictionary is unavailable:
|
|
28
|
+
* - on **web**, always — jieba-wasm ships no IDF data;
|
|
29
|
+
* - on **native**, only in builds that opted out of bundling it via `RN_JIEBA_EXCLUDE_IDF_DICT=1`
|
|
30
|
+
* (iOS) or `rnJiebaExcludeIdfDict=true` (Android), which drops ~5MB from the app.
|
|
31
|
+
*
|
|
32
|
+
* Guard it in cross-platform code, or in any app that may be built with the dictionary excluded.
|
|
33
|
+
*/
|
|
22
34
|
export function extract(sentence, topK = 5) {
|
|
23
35
|
return Jieba.extract(sentence, topK);
|
|
24
36
|
}
|
package/lib/module/jieba.js.map
CHANGED
|
@@ -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,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;
|
|
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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,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":[]}
|
|
@@ -12,6 +12,17 @@ export declare function cutForSearch(sentence: string, hmm?: boolean): string[];
|
|
|
12
12
|
export declare function cutHMM(sentence: string): string[];
|
|
13
13
|
export declare function cutSmall(sentence: string, maxWordLen: number): string[];
|
|
14
14
|
export declare function tag(sentence: string): Tagged[];
|
|
15
|
+
/**
|
|
16
|
+
* TF-IDF keyword extraction.
|
|
17
|
+
*
|
|
18
|
+
* Requires the IDF dictionary, which is loaded lazily on the first call (every other API works
|
|
19
|
+
* without it). Throws when that dictionary is unavailable:
|
|
20
|
+
* - on **web**, always — jieba-wasm ships no IDF data;
|
|
21
|
+
* - on **native**, only in builds that opted out of bundling it via `RN_JIEBA_EXCLUDE_IDF_DICT=1`
|
|
22
|
+
* (iOS) or `rnJiebaExcludeIdfDict=true` (Android), which drops ~5MB from the app.
|
|
23
|
+
*
|
|
24
|
+
* Guard it in cross-platform code, or in any app that may be built with the dictionary excluded.
|
|
25
|
+
*/
|
|
15
26
|
export declare function extract(sentence: string, topK?: number): Keyword[];
|
|
16
27
|
export declare function insertUserWord(word: string, tag?: string): boolean;
|
|
17
28
|
export declare function find(word: string): boolean;
|
|
@@ -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,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"}
|
|
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;;;;;;;;;;GAUG;AACH,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.
|
|
3
|
+
"version": "0.4.1",
|
|
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",
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
"ios/generated",
|
|
27
27
|
"cpp/JiebaImpl.h",
|
|
28
28
|
"cpp/JiebaImpl.cpp",
|
|
29
|
+
"cpp/JiebaEngine.h",
|
|
30
|
+
"cpp/JiebaEngine.cpp",
|
|
29
31
|
"cpp/JiebaDictAndroid.h",
|
|
30
32
|
"cpp/JiebaDictAndroid.cpp",
|
|
31
33
|
"cpp/cppjieba/LICENSE",
|
|
@@ -53,7 +55,8 @@
|
|
|
53
55
|
"test": "jest",
|
|
54
56
|
"release": "release-it --only-version",
|
|
55
57
|
"web": "vite",
|
|
56
|
-
"build:web": "vite build"
|
|
58
|
+
"build:web": "vite build",
|
|
59
|
+
"prepack": "node ./scripts/verify-pack-contents.mjs"
|
|
57
60
|
},
|
|
58
61
|
"keywords": [
|
|
59
62
|
"react-native",
|
package/src/jieba.ts
CHANGED
|
@@ -27,6 +27,17 @@ export function tag(sentence: string): Tagged[] {
|
|
|
27
27
|
return Jieba.tag(sentence);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* TF-IDF keyword extraction.
|
|
32
|
+
*
|
|
33
|
+
* Requires the IDF dictionary, which is loaded lazily on the first call (every other API works
|
|
34
|
+
* without it). Throws when that dictionary is unavailable:
|
|
35
|
+
* - on **web**, always — jieba-wasm ships no IDF data;
|
|
36
|
+
* - on **native**, only in builds that opted out of bundling it via `RN_JIEBA_EXCLUDE_IDF_DICT=1`
|
|
37
|
+
* (iOS) or `rnJiebaExcludeIdfDict=true` (Android), which drops ~5MB from the app.
|
|
38
|
+
*
|
|
39
|
+
* Guard it in cross-platform code, or in any app that may be built with the dictionary excluded.
|
|
40
|
+
*/
|
|
30
41
|
export function extract(sentence: string, topK: number = 5): Keyword[] {
|
|
31
42
|
return Jieba.extract(sentence, topK);
|
|
32
43
|
}
|