mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-06-15 19:00:15 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 82172e68e3 | |||
| 24bc8404fe | |||
| 1a1f816872 |
@@ -109,7 +109,11 @@ Deno.test("daemon: ignore rules behaviour", async (t) => {
|
||||
await runCliOrFail(vaultDir, "--settings", settingsFile, "mirror");
|
||||
|
||||
const dbList = await runCliOrFail(vaultDir, "--settings", settingsFile, "ls");
|
||||
assertNotContains(dbList, "debug.log", "debug.log (ignored via .gitignore import) was unexpectedly synced to database");
|
||||
assertNotContains(
|
||||
dbList,
|
||||
"debug.log",
|
||||
"debug.log (ignored via .gitignore import) was unexpectedly synced to database"
|
||||
);
|
||||
assertContains(dbList, "regular.md", "regular.md was not synced normally alongside .gitignore import rules");
|
||||
console.log("[PASS] Case 3 verified successfully");
|
||||
});
|
||||
|
||||
@@ -46,9 +46,7 @@ Deno.test("decoupled database and vault", async () => {
|
||||
console.log("[INFO] applying CouchDB environment variables to settings");
|
||||
await applyCouchdbSettings(settingsFile, uri, user, password, dbname);
|
||||
} else {
|
||||
console.warn(
|
||||
"[WARN] CouchDB environment variables are not fully set. Push and pull operations may fail."
|
||||
);
|
||||
console.warn("[WARN] CouchDB environment variables are not fully set. Push and pull operations may fail.");
|
||||
await markSettingsConfigured(settingsFile);
|
||||
}
|
||||
|
||||
@@ -59,29 +57,11 @@ Deno.test("decoupled database and vault", async () => {
|
||||
|
||||
// 1. Test push command with decoupled vault directory
|
||||
console.log(`[INFO] push with decoupled vault -> ${REMOTE_PATH}`);
|
||||
await runCliOrFail(
|
||||
dbDir,
|
||||
"--vault",
|
||||
vaultDir,
|
||||
"--settings",
|
||||
settingsFile,
|
||||
"push",
|
||||
srcFile,
|
||||
REMOTE_PATH
|
||||
);
|
||||
await runCliOrFail(dbDir, "--vault", vaultDir, "--settings", settingsFile, "push", srcFile, REMOTE_PATH);
|
||||
|
||||
// 2. Test pull command with decoupled vault directory
|
||||
console.log(`[INFO] pull with decoupled vault <- ${REMOTE_PATH}`);
|
||||
await runCliOrFail(
|
||||
dbDir,
|
||||
"--vault",
|
||||
vaultDir,
|
||||
"--settings",
|
||||
settingsFile,
|
||||
"pull",
|
||||
REMOTE_PATH,
|
||||
pulledFile
|
||||
);
|
||||
await runCliOrFail(dbDir, "--vault", vaultDir, "--settings", settingsFile, "pull", REMOTE_PATH, pulledFile);
|
||||
|
||||
const pulled = await Deno.readTextFile(pulledFile);
|
||||
assertEquals(pulled, content, "push/pull roundtrip with decoupled vault content mismatch");
|
||||
@@ -93,14 +73,7 @@ Deno.test("decoupled database and vault", async () => {
|
||||
|
||||
// 4. Test mirror command with decoupled vault directory
|
||||
console.log("[INFO] mirror with decoupled vault");
|
||||
await runCliOrFail(
|
||||
dbDir,
|
||||
"--vault",
|
||||
vaultDir,
|
||||
"--settings",
|
||||
settingsFile,
|
||||
"mirror"
|
||||
);
|
||||
await runCliOrFail(dbDir, "--vault", vaultDir, "--settings", settingsFile, "mirror");
|
||||
|
||||
const restoredFile = join(vaultDir, REMOTE_PATH);
|
||||
const restored = await Deno.readTextFile(restoredFile);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { getOptimalLoopbackIp } from "./helpers/net.ts";
|
||||
Deno.test("p2p-peers: discovers host through local relay", async () => {
|
||||
const loopbackIp = await getOptimalLoopbackIp();
|
||||
const loopbackHost = loopbackIp === "::1" ? "[::1]" : loopbackIp;
|
||||
|
||||
|
||||
const relay = Deno.env.get("RELAY") ?? `ws://${loopbackHost}:4000/`;
|
||||
const roomId = Deno.env.get("ROOM_ID") ?? `room-${Date.now()}`;
|
||||
const passphrase = Deno.env.get("PASSPHRASE") ?? "test";
|
||||
|
||||
@@ -15,7 +15,7 @@ import { getOptimalLoopbackIp } from "./helpers/net.ts";
|
||||
Deno.test("p2p-sync: discovers peer and completes sync", async () => {
|
||||
const loopbackIp = await getOptimalLoopbackIp();
|
||||
const loopbackHost = loopbackIp === "::1" ? "[::1]" : loopbackIp;
|
||||
|
||||
|
||||
const relay = Deno.env.get("RELAY") ?? `ws://${loopbackHost}:4000/`;
|
||||
const roomId = Deno.env.get("ROOM_ID") ?? `room-${Date.now()}`;
|
||||
const passphrase = Deno.env.get("PASSPHRASE") ?? "test";
|
||||
|
||||
@@ -15,7 +15,7 @@ import { getOptimalLoopbackIp } from "./helpers/net.ts";
|
||||
Deno.test("p2p: three nodes detect and resolve conflicts", async () => {
|
||||
const loopbackIp = await getOptimalLoopbackIp();
|
||||
const loopbackHost = loopbackIp === "::1" ? "[::1]" : loopbackIp;
|
||||
|
||||
|
||||
const relay = Deno.env.get("RELAY") ?? `ws://${loopbackHost}:4000/`;
|
||||
const roomId = Deno.env.get("ROOM_ID") ?? `room-${Date.now()}`;
|
||||
const passphrase = Deno.env.get("PASSPHRASE") ?? "test";
|
||||
|
||||
@@ -61,11 +61,7 @@ Deno.test("remote management commands", async () => {
|
||||
// 1. remote-status outputs valid JSON with CouchDB details
|
||||
console.log("[CASE] remote-status outputs valid JSON with CouchDB details");
|
||||
const statusOutput = await runCliCombinedOrFail(vaultDir, "--settings", settingsFile, "remote-status");
|
||||
assertContains(
|
||||
statusOutput,
|
||||
`"db_name": "${dbname}"`,
|
||||
"remote-status should return JSON containing db_name"
|
||||
);
|
||||
assertContains(statusOutput, `"db_name": "${dbname}"`, "remote-status should return JSON containing db_name");
|
||||
console.log("[PASS] remote-status verified");
|
||||
|
||||
// 2. lock-remote locks and verifies state
|
||||
|
||||
+1
-1
Submodule src/lib updated: 5a552b3ec4...29b552f34b
@@ -124,6 +124,7 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
||||
await this.services.database.openDatabase({
|
||||
databaseEvents: this.services.databaseEvents,
|
||||
replicator: this.services.replicator,
|
||||
replication: this.services.replication,
|
||||
});
|
||||
// this.core.$$realizeSettingSyncMode = this.core.$$realizeSettingSyncMode.bind(this);
|
||||
// this.$$parseReplicationResult = this.$$parseReplicationResult.bind(this);
|
||||
|
||||
@@ -3,6 +3,12 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
|
||||
|
||||
The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Improved
|
||||
- Now chunk waiting timeout is adaptive
|
||||
- Fetch chunks on demand now respects network conditions. Wait until the replication and network requests have been finished.
|
||||
|
||||
## 0.25.75
|
||||
|
||||
13th June, 2026
|
||||
|
||||
+109
-95
@@ -1,14 +1,14 @@
|
||||
import * as acorn from 'acorn';
|
||||
import fs from 'fs';
|
||||
import * as acorn from "acorn";
|
||||
import fs from "fs";
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
let file = 'main.js';
|
||||
let file = "main.js";
|
||||
let target = 2018;
|
||||
let ios = null;
|
||||
|
||||
// Help menu
|
||||
if (args.includes('--help') || args.includes('-h')) {
|
||||
if (args.includes("--help") || args.includes("-h")) {
|
||||
console.log(`Usage: node utils/check-compatibility.js [options]
|
||||
Options:
|
||||
--file <path> Path to the bundle file to check (default: main.js)
|
||||
@@ -27,13 +27,13 @@ Options:
|
||||
}
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--file' && args[i + 1]) {
|
||||
if (args[i] === "--file" && args[i + 1]) {
|
||||
file = args[i + 1];
|
||||
i++;
|
||||
} else if (args[i] === '--target' && args[i + 1]) {
|
||||
} else if (args[i] === "--target" && args[i + 1]) {
|
||||
target = parseInt(args[i + 1], 10);
|
||||
i++;
|
||||
} else if (args[i] === '--ios' && args[i + 1]) {
|
||||
} else if (args[i] === "--ios" && args[i + 1]) {
|
||||
ios = parseFloat(args[i + 1]);
|
||||
i++;
|
||||
}
|
||||
@@ -69,22 +69,22 @@ if (ios !== null) {
|
||||
|
||||
// Override defaults with explicit command line options if specified
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--allow-dynamic-import') allowDynamicImport = true;
|
||||
else if (args[i] === '--no-allow-dynamic-import') allowDynamicImport = false;
|
||||
else if (args[i] === '--allow-bigint') allowBigInt = true;
|
||||
else if (args[i] === '--no-allow-bigint') allowBigInt = false;
|
||||
else if (args[i] === '--allow-numeric-separator') allowNumericSeparator = true;
|
||||
else if (args[i] === '--no-allow-numeric-separator') allowNumericSeparator = false;
|
||||
else if (args[i] === '--allow-class-fields') allowClassFields = true;
|
||||
else if (args[i] === '--no-allow-class-fields') allowClassFields = false;
|
||||
else if (args[i] === '--allow-class-static-blocks') allowClassStaticBlocks = true;
|
||||
else if (args[i] === '--no-allow-class-static-blocks') allowClassStaticBlocks = false;
|
||||
else if (args[i] === '--allow-regexp-lookbehind') allowRegexpLookbehind = true;
|
||||
else if (args[i] === '--no-allow-regexp-lookbehind') allowRegexpLookbehind = false;
|
||||
else if (args[i] === '--allow-regexp-indices') allowRegexpIndices = true;
|
||||
else if (args[i] === '--no-allow-regexp-indices') allowRegexpIndices = false;
|
||||
else if (args[i] === '--allow-regexp-v-flag') allowRegexpVFlag = true;
|
||||
else if (args[i] === '--no-allow-regexp-v-flag') allowRegexpVFlag = false;
|
||||
if (args[i] === "--allow-dynamic-import") allowDynamicImport = true;
|
||||
else if (args[i] === "--no-allow-dynamic-import") allowDynamicImport = false;
|
||||
else if (args[i] === "--allow-bigint") allowBigInt = true;
|
||||
else if (args[i] === "--no-allow-bigint") allowBigInt = false;
|
||||
else if (args[i] === "--allow-numeric-separator") allowNumericSeparator = true;
|
||||
else if (args[i] === "--no-allow-numeric-separator") allowNumericSeparator = false;
|
||||
else if (args[i] === "--allow-class-fields") allowClassFields = true;
|
||||
else if (args[i] === "--no-allow-class-fields") allowClassFields = false;
|
||||
else if (args[i] === "--allow-class-static-blocks") allowClassStaticBlocks = true;
|
||||
else if (args[i] === "--no-allow-class-static-blocks") allowClassStaticBlocks = false;
|
||||
else if (args[i] === "--allow-regexp-lookbehind") allowRegexpLookbehind = true;
|
||||
else if (args[i] === "--no-allow-regexp-lookbehind") allowRegexpLookbehind = false;
|
||||
else if (args[i] === "--allow-regexp-indices") allowRegexpIndices = true;
|
||||
else if (args[i] === "--no-allow-regexp-indices") allowRegexpIndices = false;
|
||||
else if (args[i] === "--allow-regexp-v-flag") allowRegexpVFlag = true;
|
||||
else if (args[i] === "--no-allow-regexp-v-flag") allowRegexpVFlag = false;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
@@ -92,35 +92,35 @@ if (!fs.existsSync(file)) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const code = fs.readFileSync(file, 'utf8');
|
||||
const code = fs.readFileSync(file, "utf8");
|
||||
let ast;
|
||||
|
||||
const targetInfo = ios !== null ? `iOS ${ios}` : `ES${target}`;
|
||||
console.log(`Parsing '${file}' to inspect compatibility (target ${targetInfo})...`);
|
||||
console.log(`Rules:
|
||||
Dynamic Import: ${allowDynamicImport ? 'Allowed' : 'Prohibited'}
|
||||
BigInt: ${allowBigInt ? 'Allowed' : 'Prohibited'}
|
||||
Numeric Separators: ${allowNumericSeparator ? 'Allowed' : 'Prohibited'}
|
||||
Class Fields: ${allowClassFields ? 'Allowed' : 'Prohibited'}
|
||||
Class Static Block: ${allowClassStaticBlocks ? 'Allowed' : 'Prohibited'}
|
||||
RegExp Lookbehind: ${allowRegexpLookbehind ? 'Allowed' : 'Prohibited'}
|
||||
RegExp Indices (d): ${allowRegexpIndices ? 'Allowed' : 'Prohibited'}
|
||||
RegExp Unicode (v): ${allowRegexpVFlag ? 'Allowed' : 'Prohibited'}
|
||||
Dynamic Import: ${allowDynamicImport ? "Allowed" : "Prohibited"}
|
||||
BigInt: ${allowBigInt ? "Allowed" : "Prohibited"}
|
||||
Numeric Separators: ${allowNumericSeparator ? "Allowed" : "Prohibited"}
|
||||
Class Fields: ${allowClassFields ? "Allowed" : "Prohibited"}
|
||||
Class Static Block: ${allowClassStaticBlocks ? "Allowed" : "Prohibited"}
|
||||
RegExp Lookbehind: ${allowRegexpLookbehind ? "Allowed" : "Prohibited"}
|
||||
RegExp Indices (d): ${allowRegexpIndices ? "Allowed" : "Prohibited"}
|
||||
RegExp Unicode (v): ${allowRegexpVFlag ? "Allowed" : "Prohibited"}
|
||||
`);
|
||||
|
||||
try {
|
||||
ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'script' });
|
||||
ast = acorn.parse(code, { ecmaVersion: "latest", sourceType: "script" });
|
||||
} catch (err) {
|
||||
console.error(`Syntax Error: Failed to parse '${file}' due to a syntax issue:`);
|
||||
console.error(err.message);
|
||||
if (err.pos !== undefined) {
|
||||
const line = code.substring(0, err.pos).split('\n').length;
|
||||
const line = code.substring(0, err.pos).split("\n").length;
|
||||
console.error(`Location: line ${line}, character ${err.pos}`);
|
||||
const start = Math.max(0, err.pos - 50);
|
||||
const end = Math.min(code.length, err.pos + 50);
|
||||
console.error('Context around error:');
|
||||
console.error("Context around error:");
|
||||
console.error(code.substring(start, end));
|
||||
console.error(' '.repeat(err.pos - start) + '^');
|
||||
console.error(" ".repeat(err.pos - start) + "^");
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -131,14 +131,14 @@ const violations = [];
|
||||
function hasLookbehind(pattern) {
|
||||
let index = 0;
|
||||
while (true) {
|
||||
const match = pattern.indexOf('(?<=', index);
|
||||
const match2 = pattern.indexOf('(?<!', index);
|
||||
const pos = (match !== -1 && match2 !== -1) ? Math.min(match, match2) : (match !== -1 ? match : match2);
|
||||
const match = pattern.indexOf("(?<=", index);
|
||||
const match2 = pattern.indexOf("(?<!", index);
|
||||
const pos = match !== -1 && match2 !== -1 ? Math.min(match, match2) : match !== -1 ? match : match2;
|
||||
if (pos === -1) break;
|
||||
|
||||
|
||||
let backslashes = 0;
|
||||
for (let i = pos - 1; i >= 0; i--) {
|
||||
if (pattern[i] === '\\') backslashes++;
|
||||
if (pattern[i] === "\\") backslashes++;
|
||||
else break;
|
||||
}
|
||||
if (backslashes % 2 === 0) {
|
||||
@@ -150,178 +150,192 @@ function hasLookbehind(pattern) {
|
||||
}
|
||||
|
||||
function checkNode(node) {
|
||||
if (!node || typeof node !== 'object') return;
|
||||
if (!node || typeof node !== "object") return;
|
||||
|
||||
if (node.type) {
|
||||
// 1. Optional catch binding (ES2019 / iOS 11.3+)
|
||||
if (node.type === 'CatchClause' && !node.param) {
|
||||
if (node.type === "CatchClause" && !node.param) {
|
||||
if (target < 2019 && ios === null) {
|
||||
violations.push({
|
||||
feature: 'Optional catch binding (ES2019 / iOS 11.3+)',
|
||||
feature: "Optional catch binding (ES2019 / iOS 11.3+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
} else if (ios !== null && ios < 11.3) {
|
||||
violations.push({
|
||||
feature: 'Optional catch binding (iOS 11.3+)',
|
||||
feature: "Optional catch binding (iOS 11.3+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Dynamic import (ES2020 / iOS 11.3+)
|
||||
if (node.type === 'ImportExpression') {
|
||||
if (node.type === "ImportExpression") {
|
||||
if (!allowDynamicImport) {
|
||||
violations.push({
|
||||
feature: 'Dynamic import (ES2020 / iOS 11.3+)',
|
||||
feature: "Dynamic import (ES2020 / iOS 11.3+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. import.meta (ES2020 / iOS 11.3+)
|
||||
if (node.type === 'MetaProperty' && node.meta && node.meta.name === 'import') {
|
||||
if (node.type === "MetaProperty" && node.meta && node.meta.name === "import") {
|
||||
if (!allowDynamicImport) {
|
||||
violations.push({
|
||||
feature: 'import.meta (ES2020 / iOS 11.3+)',
|
||||
feature: "import.meta (ES2020 / iOS 11.3+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Optional chaining (ES2020 / iOS 13.4+)
|
||||
if (node.type === 'ChainExpression') {
|
||||
if (node.type === "ChainExpression") {
|
||||
const isProhibited = ios !== null ? ios < 13.4 : target < 2020;
|
||||
if (isProhibited) {
|
||||
violations.push({
|
||||
feature: 'Optional chaining (ES2020 / iOS 13.4+)',
|
||||
feature: "Optional chaining (ES2020 / iOS 13.4+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Nullish coalescing (ES2020 / iOS 13.4+)
|
||||
if (node.type === 'LogicalExpression' && node.operator === '??') {
|
||||
if (node.type === "LogicalExpression" && node.operator === "??") {
|
||||
const isProhibited = ios !== null ? ios < 13.4 : target < 2020;
|
||||
if (isProhibited) {
|
||||
violations.push({
|
||||
feature: 'Nullish coalescing (ES2020 / iOS 13.4+)',
|
||||
feature: "Nullish coalescing (ES2020 / iOS 13.4+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 6. BigInt literal (ES2020 / iOS 14.0+)
|
||||
if (node.type === 'Literal' && node.bigint !== undefined) {
|
||||
if (node.type === "Literal" && node.bigint !== undefined) {
|
||||
if (!allowBigInt) {
|
||||
violations.push({
|
||||
feature: 'BigInt literal (ES2020 / iOS 14.0+)',
|
||||
feature: "BigInt literal (ES2020 / iOS 14.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Logical assignment (ES2021 / iOS 14.0+)
|
||||
if (node.type === 'AssignmentExpression' && ['||=', '&&=', '??='].includes(node.operator)) {
|
||||
if (node.type === "AssignmentExpression" && ["||=", "&&=", "??="].includes(node.operator)) {
|
||||
const isProhibited = ios !== null ? ios < 14.0 : target < 2021;
|
||||
if (isProhibited) {
|
||||
violations.push({
|
||||
feature: `Logical assignment operator '${node.operator}' (ES2021 / iOS 14.0+)`,
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Numeric separators (ES2021 / iOS 14.0+)
|
||||
if (node.type === 'Literal' && typeof node.value === 'number' && node.raw && node.raw.includes('_')) {
|
||||
if (node.type === "Literal" && typeof node.value === "number" && node.raw && node.raw.includes("_")) {
|
||||
if (!allowNumericSeparator) {
|
||||
violations.push({
|
||||
feature: 'Numeric separator (ES2021 / iOS 14.0+)',
|
||||
feature: "Numeric separator (ES2021 / iOS 14.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Class Fields (ES2022 / iOS 14.0+ public, iOS 14.5+ private/static)
|
||||
if (node.type === 'PropertyDefinition') {
|
||||
if (node.type === "PropertyDefinition") {
|
||||
if (!allowClassFields) {
|
||||
const requiredVersion = node.key.type === 'PrivateIdentifier' || node.static ? 'iOS 14.5+' : 'iOS 14.0+';
|
||||
const requiredVersion =
|
||||
node.key.type === "PrivateIdentifier" || node.static ? "iOS 14.5+" : "iOS 14.0+";
|
||||
violations.push({
|
||||
feature: `Class field definition '${node.key.name || node.key.value || '#private'}' (ES2022 / ${requiredVersion})`,
|
||||
feature: `Class field definition '${node.key.name || node.key.value || "#private"}' (ES2022 / ${requiredVersion})`,
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Class Static Initialization Blocks (ES2022 / iOS 16.4+)
|
||||
if (node.type === 'StaticBlock') {
|
||||
if (node.type === "StaticBlock") {
|
||||
if (!allowClassStaticBlocks) {
|
||||
violations.push({
|
||||
feature: 'Class static initialization block (ES2022 / iOS 16.4+)',
|
||||
feature: "Class static initialization block (ES2022 / iOS 16.4+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 11. RegExp lookbehind assertions (ES2018 / iOS 16.4+)
|
||||
if (node.type === 'Literal' && node.regex) {
|
||||
if (node.type === "Literal" && node.regex) {
|
||||
if (!allowRegexpLookbehind && hasLookbehind(node.regex.pattern)) {
|
||||
violations.push({
|
||||
feature: 'RegExp Lookbehind assertion (iOS 16.4+)',
|
||||
feature: "RegExp Lookbehind assertion (iOS 16.4+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
if (!allowRegexpIndices && node.regex.flags.includes('d')) {
|
||||
if (!allowRegexpIndices && node.regex.flags.includes("d")) {
|
||||
violations.push({
|
||||
feature: "RegExp 'd' (indices) flag (ES2022 / iOS 15.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
if (!allowRegexpVFlag && node.regex.flags.includes('v')) {
|
||||
if (!allowRegexpVFlag && node.regex.flags.includes("v")) {
|
||||
violations.push({
|
||||
feature: "RegExp 'v' (Unicode properties) flag (ES2024 / iOS 17.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ((node.type === 'NewExpression' || node.type === 'CallExpression') && node.callee && node.callee.name === 'RegExp') {
|
||||
if (!allowRegexpLookbehind && node.arguments[0] && node.arguments[0].type === 'Literal' && typeof node.arguments[0].value === 'string') {
|
||||
if (
|
||||
(node.type === "NewExpression" || node.type === "CallExpression") &&
|
||||
node.callee &&
|
||||
node.callee.name === "RegExp"
|
||||
) {
|
||||
if (
|
||||
!allowRegexpLookbehind &&
|
||||
node.arguments[0] &&
|
||||
node.arguments[0].type === "Literal" &&
|
||||
typeof node.arguments[0].value === "string"
|
||||
) {
|
||||
if (hasLookbehind(node.arguments[0].value)) {
|
||||
violations.push({
|
||||
feature: 'RegExp Lookbehind assertion (iOS 16.4+)',
|
||||
feature: "RegExp Lookbehind assertion (iOS 16.4+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (node.arguments[1] && node.arguments[1].type === 'Literal' && typeof node.arguments[1].value === 'string') {
|
||||
if (
|
||||
node.arguments[1] &&
|
||||
node.arguments[1].type === "Literal" &&
|
||||
typeof node.arguments[1].value === "string"
|
||||
) {
|
||||
const flags = node.arguments[1].value;
|
||||
if (!allowRegexpIndices && flags.includes('d')) {
|
||||
if (!allowRegexpIndices && flags.includes("d")) {
|
||||
violations.push({
|
||||
feature: "RegExp 'd' (indices) flag (ES2022 / iOS 15.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
if (!allowRegexpVFlag && flags.includes('v')) {
|
||||
if (!allowRegexpVFlag && flags.includes("v")) {
|
||||
violations.push({
|
||||
feature: "RegExp 'v' (Unicode properties) flag (ES2024 / iOS 17.0+)",
|
||||
pos: node.start,
|
||||
node
|
||||
node,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -329,13 +343,13 @@ function checkNode(node) {
|
||||
}
|
||||
|
||||
for (const key in node) {
|
||||
if (key === 'loc' || key === 'start' || key === 'end') continue;
|
||||
if (key === "loc" || key === "start" || key === "end") continue;
|
||||
const val = node[key];
|
||||
if (Array.isArray(val)) {
|
||||
for (const child of val) {
|
||||
checkNode(child);
|
||||
}
|
||||
} else if (val && typeof val === 'object') {
|
||||
} else if (val && typeof val === "object") {
|
||||
checkNode(val);
|
||||
}
|
||||
}
|
||||
@@ -347,14 +361,14 @@ checkNode(ast);
|
||||
if (violations.length > 0) {
|
||||
console.error(`\nCompatibility Check Failed: Found ${violations.length} prohibited features.`);
|
||||
violations.forEach((v, index) => {
|
||||
const line = code.substring(0, v.pos).split('\n').length;
|
||||
const line = code.substring(0, v.pos).split("\n").length;
|
||||
console.error(`\n[${index + 1}] Prohibited feature: ${v.feature}`);
|
||||
console.error(`Location: line ${line}, character ${v.pos}`);
|
||||
const start = Math.max(0, v.pos - 50);
|
||||
const end = Math.min(code.length, v.pos + 50);
|
||||
console.error('Context around feature:');
|
||||
console.error("Context around feature:");
|
||||
console.error(code.substring(start, end));
|
||||
console.error(' '.repeat(v.pos - start) + '^');
|
||||
console.error(" ".repeat(v.pos - start) + "^");
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user