prosemirror-rs 0.3.0 → 0.3.5
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/Cargo.toml
CHANGED
package/copy-artifact.mjs
CHANGED
|
@@ -4,23 +4,42 @@
|
|
|
4
4
|
* The filename follows the napi-rs triple convention so that index.js can
|
|
5
5
|
* load the correct binary at runtime. Called automatically by `npm run build`
|
|
6
6
|
* after `cargo build --release`.
|
|
7
|
+
*
|
|
8
|
+
* Environment variables (optional, used in CI):
|
|
9
|
+
* RUST_TARGET – cross-compilation target, e.g. "aarch64-unknown-linux-gnu"
|
|
10
|
+
* When set, the binary is looked up under
|
|
11
|
+
* target/<RUST_TARGET>/release/ instead of target/release/.
|
|
7
12
|
*/
|
|
8
|
-
import { cpSync } from 'fs';
|
|
13
|
+
import { cpSync, existsSync } from 'fs';
|
|
9
14
|
import { platform, arch } from 'os';
|
|
10
15
|
import { fileURLToPath } from 'url';
|
|
11
16
|
import { join, dirname } from 'path';
|
|
12
17
|
|
|
13
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
19
|
|
|
15
|
-
//
|
|
16
|
-
|
|
20
|
+
// ── Per-platform helper: shared library filename (without path) ───────────
|
|
21
|
+
// Windows: prosemirror_rs.dll (no lib- prefix)
|
|
22
|
+
// macOS: libprosemirror_rs.dylib
|
|
23
|
+
// Linux: libprosemirror_rs.so
|
|
24
|
+
function libFilename() {
|
|
17
25
|
const p = platform();
|
|
18
|
-
if (p === 'win32') return '.dll';
|
|
19
|
-
if (p === 'darwin') return '.dylib';
|
|
20
|
-
return '.so';
|
|
26
|
+
if (p === 'win32') return 'prosemirror_rs.dll';
|
|
27
|
+
if (p === 'darwin') return 'libprosemirror_rs.dylib';
|
|
28
|
+
return 'libprosemirror_rs.so';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ── Build directory ──────────────────────────────────────────────────────
|
|
32
|
+
// When cross-compiling, cargo places output under target/<rust_target>/release/
|
|
33
|
+
// instead of target/release/.
|
|
34
|
+
function buildDir() {
|
|
35
|
+
const rustTarget = process.env.RUST_TARGET;
|
|
36
|
+
if (rustTarget) {
|
|
37
|
+
return join(__dirname, '..', 'target', rustTarget, 'release');
|
|
38
|
+
}
|
|
39
|
+
return join(__dirname, '..', 'target', 'release');
|
|
21
40
|
}
|
|
22
41
|
|
|
23
|
-
//
|
|
42
|
+
// ── napi-rs platform triple for the output file ──────────────────────────
|
|
24
43
|
function triple() {
|
|
25
44
|
const p = platform();
|
|
26
45
|
const a = arch();
|
|
@@ -32,8 +51,18 @@ function triple() {
|
|
|
32
51
|
throw new Error(`Unsupported platform/arch: ${p}-${a}`);
|
|
33
52
|
}
|
|
34
53
|
|
|
35
|
-
const src = join(
|
|
54
|
+
const src = join(buildDir(), libFilename());
|
|
36
55
|
const dest = join(__dirname, `prosemirror-rs.${triple()}.node`);
|
|
37
56
|
|
|
57
|
+
if (!existsSync(src)) {
|
|
58
|
+
console.error(`ERROR: compiled artifact not found at ${src}`);
|
|
59
|
+
console.error('');
|
|
60
|
+
console.error('Make sure cargo build --release completed successfully first.');
|
|
61
|
+
if (process.env.RUST_TARGET) {
|
|
62
|
+
console.error(`(RUST_TARGET=${process.env.RUST_TARGET} is set)`);
|
|
63
|
+
}
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
38
67
|
cpSync(src, dest);
|
|
39
|
-
console.log(`Copied ${src} → ${dest}`);
|
|
68
|
+
console.log(`Copied ${src} → ${dest}`);
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/lib.rs
CHANGED
|
@@ -268,10 +268,15 @@ impl Editor {
|
|
|
268
268
|
/// document directly to a database without creating any intermediate
|
|
269
269
|
/// JS objects.
|
|
270
270
|
///
|
|
271
|
+
/// When `skipDefaults` is `true`, attributes whose value matches the
|
|
272
|
+
/// schema-defined default are omitted from the output ("mini" JSON).
|
|
273
|
+
///
|
|
274
|
+
/// @param skipDefaults If true, omit attributes that have default values.
|
|
271
275
|
/// @returns The document as a compact JSON string.
|
|
272
276
|
#[napi]
|
|
273
|
-
pub fn doc_json(&self) -> napi::Result<String> {
|
|
274
|
-
|
|
277
|
+
pub fn doc_json(&self, skip_defaults: Option<bool>) -> napi::Result<String> {
|
|
278
|
+
let val = self.schema.with_types(|| self.doc.to_json(skip_defaults.unwrap_or(false)));
|
|
279
|
+
serde_json::to_string(&val)
|
|
275
280
|
.map_err(|e| napi::Error::new(Status::GenericFailure, format!("Serialization error: {e}")))
|
|
276
281
|
}
|
|
277
282
|
|
package/tests/editor.test.js
CHANGED
|
@@ -81,16 +81,96 @@ test('constructor throws when doc JSON is not a node object', () => {
|
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
// ---------------------------------------------------------------------------
|
|
84
|
-
// docJson
|
|
84
|
+
// docJson with skipDefaults
|
|
85
85
|
// ---------------------------------------------------------------------------
|
|
86
86
|
|
|
87
|
-
test('docJson
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
test('docJson() without argument includes all attributes', () => {
|
|
88
|
+
// Schema with attributes that have defaults
|
|
89
|
+
const schemaWithAttrs = JSON.stringify({
|
|
90
|
+
nodes: {
|
|
91
|
+
doc: { content: 'paragraph+' },
|
|
92
|
+
paragraph: {
|
|
93
|
+
content: 'text*',
|
|
94
|
+
group: 'block',
|
|
95
|
+
attrs: { align: { default: 'left' }, indent: { default: 0 } },
|
|
96
|
+
},
|
|
97
|
+
text: { group: 'inline' },
|
|
98
|
+
},
|
|
99
|
+
marks: { strong: { attrs: { level: { default: 1 } } }, em: {} },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Document with default attributes (should be included in regular serialization)
|
|
103
|
+
const docDefault = JSON.stringify({
|
|
104
|
+
type: 'doc',
|
|
105
|
+
content: [{
|
|
106
|
+
type: 'paragraph',
|
|
107
|
+
attrs: { align: 'left', indent: 0 },
|
|
108
|
+
content: [{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: 'hello',
|
|
111
|
+
marks: [{ type: 'strong', attrs: { level: 1 } }],
|
|
112
|
+
}],
|
|
113
|
+
}],
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const editorDefault = new Editor(schemaWithAttrs, docDefault);
|
|
117
|
+
|
|
118
|
+
// Regular serialization should include all attrs
|
|
119
|
+
const fullRaw = editorDefault.docJson();
|
|
120
|
+
const full = JSON.parse(fullRaw);
|
|
121
|
+
assert.ok(fullRaw.includes('"attrs"'), `Expected attrs in full serialization: ${fullRaw}`);
|
|
122
|
+
assert.equal(full.content[0].attrs.align, 'left');
|
|
123
|
+
assert.equal(full.content[0].attrs.indent, 0);
|
|
124
|
+
assert.equal(full.content[0].content[0].marks[0].attrs.level, 1);
|
|
125
|
+
|
|
126
|
+
// Mini serialization should skip all default attributes
|
|
127
|
+
const miniRaw = editorDefault.docJson(true);
|
|
128
|
+
const mini = JSON.parse(miniRaw);
|
|
129
|
+
assert.ok(!miniRaw.includes('"attrs"'), `Expected no attrs in mini serialization: ${miniRaw}`);
|
|
130
|
+
assert.ok(!('attrs' in mini.content[0]), 'paragraph should have no attrs');
|
|
131
|
+
assert.ok(!('attrs' in mini.content[0].content[0].marks[0]), 'mark should have no attrs');
|
|
132
|
+
|
|
133
|
+
// Document with non-default attributes
|
|
134
|
+
const docCustom = JSON.stringify({
|
|
135
|
+
type: 'doc',
|
|
136
|
+
content: [{
|
|
137
|
+
type: 'paragraph',
|
|
138
|
+
attrs: { align: 'right', indent: 2 },
|
|
139
|
+
content: [{ type: 'text', text: 'world' }],
|
|
140
|
+
}],
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const editorCustom = new Editor(schemaWithAttrs, docCustom);
|
|
144
|
+
const miniCustomRaw = editorCustom.docJson(true);
|
|
145
|
+
const miniCustom = JSON.parse(miniCustomRaw);
|
|
146
|
+
// Non-default attrs should still appear
|
|
147
|
+
assert.ok(miniCustomRaw.includes('"attrs"'), `Expected attrs for non-default values: ${miniCustomRaw}`);
|
|
148
|
+
assert.equal(miniCustom.content[0].attrs.align, 'right');
|
|
149
|
+
assert.equal(miniCustom.content[0].attrs.indent, 2);
|
|
150
|
+
|
|
151
|
+
// Mix of default and non-default
|
|
152
|
+
const docMixed = JSON.stringify({
|
|
153
|
+
type: 'doc',
|
|
154
|
+
content: [{
|
|
155
|
+
type: 'paragraph',
|
|
156
|
+
attrs: { align: 'center', indent: 0 },
|
|
157
|
+
content: [{ type: 'text', text: 'mixed' }],
|
|
158
|
+
}],
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const editorMixed = new Editor(schemaWithAttrs, docMixed);
|
|
162
|
+
const miniMixedRaw = editorMixed.docJson(true);
|
|
163
|
+
const miniMixed = JSON.parse(miniMixedRaw);
|
|
164
|
+
// Only 'align' should be present (indent is default 0)
|
|
165
|
+
assert.ok(miniMixedRaw.includes('"attrs"'), `Expected attrs for partial non-default: ${miniMixedRaw}`);
|
|
166
|
+
assert.equal(miniMixed.content[0].attrs.align, 'center');
|
|
167
|
+
assert.ok(!('indent' in miniMixed.content[0].attrs), 'indent should be omitted (default value)');
|
|
168
|
+
|
|
169
|
+
// Verify backwards compatibility: no argument == false
|
|
170
|
+
const editorBC = new Editor(SCHEMA, DOC);
|
|
171
|
+
const noArg = editorBC.docJson();
|
|
172
|
+
const falseArg = editorBC.docJson(false);
|
|
173
|
+
assert.equal(noArg, falseArg, 'docJson() and docJson(false) should be identical');
|
|
94
174
|
});
|
|
95
175
|
|
|
96
176
|
// ---------------------------------------------------------------------------
|