framer-code-link 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +84 -68
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -81,11 +81,11 @@ const firstCharacterRegex = /^[a-zA-Z$_]/;
|
|
|
81
81
|
const remainingCharactersRegex = /[^a-zA-Z0-9$_]/g;
|
|
82
82
|
const onlyDotsRegex = /^\.+$/;
|
|
83
83
|
const tsxExtension = ".tsx";
|
|
84
|
-
var NameType = /* @__PURE__ */ function(NameType
|
|
85
|
-
NameType
|
|
86
|
-
NameType
|
|
87
|
-
NameType
|
|
88
|
-
return NameType
|
|
84
|
+
var NameType = /* @__PURE__ */ function(NameType) {
|
|
85
|
+
NameType["Variable"] = "Variable";
|
|
86
|
+
NameType["Selector"] = "Selector";
|
|
87
|
+
NameType["Directory"] = "Directory";
|
|
88
|
+
return NameType;
|
|
89
89
|
}(NameType || {});
|
|
90
90
|
function sanitizedName(type, name) {
|
|
91
91
|
if (!name) return null;
|
|
@@ -139,7 +139,7 @@ function pathJoin(...parts) {
|
|
|
139
139
|
});
|
|
140
140
|
return res;
|
|
141
141
|
}
|
|
142
|
-
function normalizePath(filePath) {
|
|
142
|
+
function normalizePath$1(filePath) {
|
|
143
143
|
if (!filePath) return "";
|
|
144
144
|
const isAbsolute = filePath.startsWith("/");
|
|
145
145
|
const segments = filePath.replace(/\\/g, "/").split("/");
|
|
@@ -156,6 +156,13 @@ function normalizePath(filePath) {
|
|
|
156
156
|
if (isAbsolute) return `/${normalized}`;
|
|
157
157
|
return normalized;
|
|
158
158
|
}
|
|
159
|
+
function normalizeCodeFilePath(filePath) {
|
|
160
|
+
const normalized = normalizePath$1(filePath);
|
|
161
|
+
return normalized.startsWith("/") ? normalized.slice(1) : normalized;
|
|
162
|
+
}
|
|
163
|
+
function canonicalFileName(filePath) {
|
|
164
|
+
return normalizeCodeFilePath(filePath);
|
|
165
|
+
}
|
|
159
166
|
function sanitizeFilePath(input, capitalizeReactComponent = true) {
|
|
160
167
|
const trimmed = input.trim();
|
|
161
168
|
const [inputName, extension] = splitExtension(filename(trimmed));
|
|
@@ -170,10 +177,17 @@ function sanitizeFilePath(input, capitalizeReactComponent = true) {
|
|
|
170
177
|
extension
|
|
171
178
|
};
|
|
172
179
|
}
|
|
173
|
-
function isSupportedExtension(filePath) {
|
|
180
|
+
function isSupportedExtension$1(filePath) {
|
|
174
181
|
return /\.(tsx?|jsx?|json)$/i.test(filePath);
|
|
175
182
|
}
|
|
176
183
|
/**
|
|
184
|
+
* Returns a normalized, lowercase key for case-insensitive file lookups.
|
|
185
|
+
* Use this for Map keys on operating systems where "File.tsx" and "file.tsx" are the same file.
|
|
186
|
+
*/
|
|
187
|
+
function fileKeyForLookup(filePath) {
|
|
188
|
+
return canonicalFileName(filePath).toLowerCase();
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
177
191
|
* Pluralize a word based on count
|
|
178
192
|
* @example pluralize(1, "file") => "1 file"
|
|
179
193
|
* @example pluralize(3, "file") => "3 files"
|
|
@@ -275,12 +289,12 @@ var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
275
289
|
//#endregion
|
|
276
290
|
//#region src/utils/logging.ts
|
|
277
291
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
278
|
-
let LogLevel = /* @__PURE__ */ function(LogLevel
|
|
279
|
-
LogLevel
|
|
280
|
-
LogLevel
|
|
281
|
-
LogLevel
|
|
282
|
-
LogLevel
|
|
283
|
-
return LogLevel
|
|
292
|
+
let LogLevel = /* @__PURE__ */ function(LogLevel) {
|
|
293
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
294
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
295
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
296
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
297
|
+
return LogLevel;
|
|
284
298
|
}({});
|
|
285
299
|
let currentLevel = LogLevel.INFO;
|
|
286
300
|
let lastMessage = "";
|
|
@@ -325,9 +339,9 @@ function logWithDedupe(message, writer) {
|
|
|
325
339
|
/**
|
|
326
340
|
* Print the startup banner - one colored line
|
|
327
341
|
*/
|
|
328
|
-
function banner(version
|
|
342
|
+
function banner(version, port) {
|
|
329
343
|
console.log();
|
|
330
|
-
let message = ` ${import_picocolors.default.cyan(import_picocolors.default.bold("⚡ Code Link"))} ${import_picocolors.default.dim(`v${version
|
|
344
|
+
let message = ` ${import_picocolors.default.cyan(import_picocolors.default.bold("⚡ Code Link"))} ${import_picocolors.default.dim(`v${version}`)}`;
|
|
331
345
|
if (currentLevel <= LogLevel.DEBUG) message += ` ${import_picocolors.default.dim("Port")} ${import_picocolors.default.yellow(port)}`;
|
|
332
346
|
console.log(message);
|
|
333
347
|
console.log();
|
|
@@ -597,7 +611,7 @@ function getRelativePath(projectDir, absolutePath) {
|
|
|
597
611
|
* - Resolving . and .. segments
|
|
598
612
|
* - Removing duplicate slashes
|
|
599
613
|
*/
|
|
600
|
-
function normalizePath
|
|
614
|
+
function normalizePath(filePath) {
|
|
601
615
|
if (!filePath) return "";
|
|
602
616
|
const isAbsolute = filePath.startsWith("/");
|
|
603
617
|
const segments = filePath.replace(/\\/g, "/").split("/");
|
|
@@ -635,7 +649,7 @@ const SUPPORTED_EXTENSIONS$1 = [
|
|
|
635
649
|
];
|
|
636
650
|
const DEFAULT_EXTENSION$1 = ".tsx";
|
|
637
651
|
function normalizePersistedFileName(fileName) {
|
|
638
|
-
let normalized = normalizePath
|
|
652
|
+
let normalized = normalizePath(fileName.trim());
|
|
639
653
|
if (!SUPPORTED_EXTENSIONS$1.some((ext) => normalized.toLowerCase().endsWith(ext))) normalized = `${normalized}${DEFAULT_EXTENSION$1}`;
|
|
640
654
|
return normalized;
|
|
641
655
|
}
|
|
@@ -713,10 +727,6 @@ const SUPPORTED_EXTENSIONS = [
|
|
|
713
727
|
];
|
|
714
728
|
const DEFAULT_EXTENSION = ".tsx";
|
|
715
729
|
const DEFAULT_REMOTE_DRIFT_MS = 2e3;
|
|
716
|
-
/** Normalize file name for case-insensitive comparison (macOS/Windows compat) */
|
|
717
|
-
function normalizeForComparison(fileName) {
|
|
718
|
-
return fileName.toLowerCase();
|
|
719
|
-
}
|
|
720
730
|
/**
|
|
721
731
|
* Lists all supported files in the files directory
|
|
722
732
|
*/
|
|
@@ -730,8 +740,8 @@ async function listFiles(filesDir) {
|
|
|
730
740
|
await walk(entryPath);
|
|
731
741
|
continue;
|
|
732
742
|
}
|
|
733
|
-
if (!isSupportedExtension
|
|
734
|
-
const sanitizedPath = sanitizeFilePath(normalizePath(path.relative(filesDir, entryPath)), false).path;
|
|
743
|
+
if (!isSupportedExtension(entry.name)) continue;
|
|
744
|
+
const sanitizedPath = sanitizeFilePath(normalizePath$1(path.relative(filesDir, entryPath)), false).path;
|
|
735
745
|
try {
|
|
736
746
|
const [content, stats] = await Promise.all([fs.readFile(entryPath, "utf-8"), fs.stat(entryPath)]);
|
|
737
747
|
files.push({
|
|
@@ -759,17 +769,17 @@ async function detectConflicts(remoteFiles, filesDir, options = {}) {
|
|
|
759
769
|
const detect = options.detectConflicts ?? true;
|
|
760
770
|
const preferRemote = options.preferRemote ?? false;
|
|
761
771
|
const persistedState = options.persistedState;
|
|
762
|
-
const getPersistedState = (fileName) => persistedState?.get(
|
|
772
|
+
const getPersistedState = (fileName) => persistedState?.get(fileKeyForLookup(fileName));
|
|
763
773
|
debug(`Detecting conflicts for ${String(remoteFiles.length)} remote files`);
|
|
764
774
|
const localFiles = await listFiles(filesDir);
|
|
765
|
-
const localFileMap = new Map(localFiles.map((f) => [
|
|
775
|
+
const localFileMap = new Map(localFiles.map((f) => [fileKeyForLookup(f.name), f]));
|
|
766
776
|
const remoteFileMap = new Map(remoteFiles.map((f) => {
|
|
767
|
-
return [
|
|
777
|
+
return [fileKeyForLookup(resolveRemoteReference(filesDir, f.name).relativePath), f];
|
|
768
778
|
}));
|
|
769
779
|
const processedFiles = /* @__PURE__ */ new Set();
|
|
770
780
|
for (const remote of remoteFiles) {
|
|
771
781
|
const normalized = resolveRemoteReference(filesDir, remote.name);
|
|
772
|
-
const normalizedKey =
|
|
782
|
+
const normalizedKey = fileKeyForLookup(normalized.relativePath);
|
|
773
783
|
const local = localFileMap.get(normalizedKey);
|
|
774
784
|
processedFiles.add(normalizedKey);
|
|
775
785
|
const persisted = getPersistedState(normalized.relativePath);
|
|
@@ -820,7 +830,7 @@ async function detectConflicts(remoteFiles, filesDir, options = {}) {
|
|
|
820
830
|
});
|
|
821
831
|
}
|
|
822
832
|
for (const local of localFiles) {
|
|
823
|
-
const localKey =
|
|
833
|
+
const localKey = fileKeyForLookup(local.name);
|
|
824
834
|
if (!processedFiles.has(localKey)) {
|
|
825
835
|
const persisted = getPersistedState(local.name);
|
|
826
836
|
if (persisted) {
|
|
@@ -841,8 +851,8 @@ async function detectConflicts(remoteFiles, filesDir, options = {}) {
|
|
|
841
851
|
});
|
|
842
852
|
}
|
|
843
853
|
}
|
|
844
|
-
if (persistedState) for (const
|
|
845
|
-
const normalizedKey =
|
|
854
|
+
if (persistedState) for (const fileName of persistedState.keys()) {
|
|
855
|
+
const normalizedKey = fileKeyForLookup(fileName);
|
|
846
856
|
const inLocal = localFileMap.has(normalizedKey);
|
|
847
857
|
const inRemote = remoteFileMap.has(normalizedKey);
|
|
848
858
|
if (!inLocal && !inRemote) debug(`[AUTO-RESOLVE] ${fileName}: deleted on both sides, no conflict`);
|
|
@@ -855,7 +865,7 @@ async function detectConflicts(remoteFiles, filesDir, options = {}) {
|
|
|
855
865
|
};
|
|
856
866
|
}
|
|
857
867
|
function autoResolveConflicts(conflicts, versions, options = {}) {
|
|
858
|
-
const versionMap = new Map(versions.map((version
|
|
868
|
+
const versionMap = new Map(versions.map((version) => [version.fileName, version.latestRemoteVersionMs]));
|
|
859
869
|
const remoteDriftMs = options.remoteDriftMs ?? DEFAULT_REMOTE_DRIFT_MS;
|
|
860
870
|
const autoResolvedLocal = [];
|
|
861
871
|
const autoResolvedRemote = [];
|
|
@@ -875,28 +885,31 @@ function autoResolveConflicts(conflicts, versions, options = {}) {
|
|
|
875
885
|
}
|
|
876
886
|
continue;
|
|
877
887
|
}
|
|
888
|
+
if (localClean) {
|
|
889
|
+
debug(` Local clean -> REMOTE (safe to overwrite)`);
|
|
890
|
+
autoResolvedRemote.push(conflict);
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
878
893
|
if (!latestRemoteVersionMs) {
|
|
879
|
-
debug(`
|
|
894
|
+
debug(` Local modified, no remote version data -> conflict`);
|
|
880
895
|
remainingConflicts.push(conflict);
|
|
881
896
|
continue;
|
|
882
897
|
}
|
|
883
898
|
if (!lastSyncedAt) {
|
|
884
|
-
debug(`
|
|
899
|
+
debug(` Local modified, no sync timestamp -> conflict`);
|
|
885
900
|
remainingConflicts.push(conflict);
|
|
886
901
|
continue;
|
|
887
902
|
}
|
|
888
903
|
debug(` Remote: ${new Date(latestRemoteVersionMs).toISOString()}`);
|
|
889
904
|
debug(` Synced: ${new Date(lastSyncedAt).toISOString()}`);
|
|
890
905
|
const remoteUnchanged = latestRemoteVersionMs <= lastSyncedAt + remoteDriftMs;
|
|
891
|
-
|
|
906
|
+
const driftMargin = latestRemoteVersionMs - lastSyncedAt;
|
|
907
|
+
if (remoteUnchanged) {
|
|
892
908
|
debug(` Remote unchanged, local changed -> LOCAL`);
|
|
909
|
+
if (driftMargin > 0) debug(` (within drift tolerance: ${driftMargin}ms < ${remoteDriftMs}ms threshold)`);
|
|
893
910
|
autoResolvedLocal.push(conflict);
|
|
894
|
-
} else
|
|
895
|
-
debug(`
|
|
896
|
-
autoResolvedRemote.push(conflict);
|
|
897
|
-
} else if (remoteUnchanged && localClean) debug(` Both unchanged, skipping`);
|
|
898
|
-
else {
|
|
899
|
-
debug(` Both changed, real conflict`);
|
|
911
|
+
} else {
|
|
912
|
+
debug(` Both changed -> conflict (remote ahead by ${driftMargin}ms, threshold: ${remoteDriftMs}ms)`);
|
|
900
913
|
remainingConflicts.push(conflict);
|
|
901
914
|
}
|
|
902
915
|
}
|
|
@@ -973,15 +986,15 @@ function resolveRemoteReference(filesDir, rawName) {
|
|
|
973
986
|
};
|
|
974
987
|
}
|
|
975
988
|
function sanitizeRelativePath(relativePath) {
|
|
976
|
-
const trimmed = normalizePath(relativePath.trim());
|
|
989
|
+
const trimmed = normalizePath$1(relativePath.trim());
|
|
977
990
|
const sanitized = sanitizeFilePath(SUPPORTED_EXTENSIONS.some((ext) => trimmed.toLowerCase().endsWith(ext)) ? trimmed : `${trimmed}${DEFAULT_EXTENSION}`, false);
|
|
978
|
-
const normalized = normalizePath(sanitized.path);
|
|
991
|
+
const normalized = normalizePath$1(sanitized.path);
|
|
979
992
|
return {
|
|
980
993
|
relativePath: normalized,
|
|
981
994
|
extension: sanitized.extension || path.extname(normalized) || DEFAULT_EXTENSION
|
|
982
995
|
};
|
|
983
996
|
}
|
|
984
|
-
function isSupportedExtension
|
|
997
|
+
function isSupportedExtension(fileName) {
|
|
985
998
|
const lower = fileName.toLowerCase();
|
|
986
999
|
return SUPPORTED_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
987
1000
|
}
|
|
@@ -1082,8 +1095,8 @@ var Installer = class {
|
|
|
1082
1095
|
finished: (files) => {
|
|
1083
1096
|
if (files.size > 0) debug("ATA: type acquisition complete");
|
|
1084
1097
|
},
|
|
1085
|
-
errorMessage: (message, error
|
|
1086
|
-
warn(`ATA warning: ${message}`, error
|
|
1098
|
+
errorMessage: (message, error) => {
|
|
1099
|
+
warn(`ATA warning: ${message}`, error);
|
|
1087
1100
|
},
|
|
1088
1101
|
receivedFile: (code, receivedPath) => {
|
|
1089
1102
|
(async () => {
|
|
@@ -1204,9 +1217,9 @@ var Installer = class {
|
|
|
1204
1217
|
const response = await fetch(`https://registry.npmjs.org/${pkgName}`);
|
|
1205
1218
|
if (!response.ok) return;
|
|
1206
1219
|
const npmData = await response.json();
|
|
1207
|
-
const version
|
|
1208
|
-
if (!version
|
|
1209
|
-
const pkg = npmData.versions[version
|
|
1220
|
+
const version = npmData["dist-tags"]?.latest;
|
|
1221
|
+
if (!version || !npmData.versions?.[version]) return;
|
|
1222
|
+
const pkg = npmData.versions[version];
|
|
1210
1223
|
if (pkg.exports) for (const key of Object.keys(pkg.exports)) pkg.exports[key] = fixExportTypes(pkg.exports[key]);
|
|
1211
1224
|
await fs.mkdir(pkgDir, { recursive: true });
|
|
1212
1225
|
await fs.writeFile(pkgJsonPath, JSON.stringify(pkg, null, 2));
|
|
@@ -1314,11 +1327,11 @@ declare module "*.json"
|
|
|
1314
1327
|
if (await this.hasTypePackage(reactDomDir, REACT_DOM_TYPES_VERSION, reactDomFiles)) debug("📦 React DOM types (from cache)");
|
|
1315
1328
|
else await this.downloadTypePackage("@types/react-dom", REACT_DOM_TYPES_VERSION, reactDomDir, reactDomFiles);
|
|
1316
1329
|
}
|
|
1317
|
-
async hasTypePackage(destinationDir, version
|
|
1330
|
+
async hasTypePackage(destinationDir, version, files) {
|
|
1318
1331
|
try {
|
|
1319
1332
|
const pkgJsonPath = path.join(destinationDir, "package.json");
|
|
1320
1333
|
const pkgJson = await fs.readFile(pkgJsonPath, "utf-8");
|
|
1321
|
-
if (JSON.parse(pkgJson).version !== version
|
|
1334
|
+
if (JSON.parse(pkgJson).version !== version) return false;
|
|
1322
1335
|
for (const file of files) {
|
|
1323
1336
|
if (file === "package.json") continue;
|
|
1324
1337
|
await fs.access(path.join(destinationDir, file));
|
|
@@ -1328,8 +1341,8 @@ declare module "*.json"
|
|
|
1328
1341
|
return false;
|
|
1329
1342
|
}
|
|
1330
1343
|
}
|
|
1331
|
-
async downloadTypePackage(pkgName, version
|
|
1332
|
-
const baseUrl = `https://unpkg.com/${pkgName}@${version
|
|
1344
|
+
async downloadTypePackage(pkgName, version, destinationDir, files) {
|
|
1345
|
+
const baseUrl = `https://unpkg.com/${pkgName}@${version}`;
|
|
1333
1346
|
await fs.mkdir(destinationDir, { recursive: true });
|
|
1334
1347
|
await Promise.all(files.map(async (file) => {
|
|
1335
1348
|
const destination = path.join(destinationDir, file);
|
|
@@ -1388,17 +1401,17 @@ async function fetchWithRetry(url, init, retries = MAX_FETCH_RETRIES) {
|
|
|
1388
1401
|
return response;
|
|
1389
1402
|
} catch (err) {
|
|
1390
1403
|
clearTimeout(timeout);
|
|
1391
|
-
const error
|
|
1392
|
-
const isRetryable = error
|
|
1404
|
+
const error = err;
|
|
1405
|
+
const isRetryable = error.cause?.code === "ECONNRESET" || error.cause?.code === "ETIMEDOUT" || error.cause?.code === "UND_ERR_CONNECT_TIMEOUT" || error.message.includes("timeout");
|
|
1393
1406
|
if (isRetryable) checkFatalFailure(urlString);
|
|
1394
1407
|
if (attempt < retries && isRetryable) {
|
|
1395
1408
|
const delay = attempt * 1e3;
|
|
1396
|
-
warn(`Fetch failed (${error
|
|
1409
|
+
warn(`Fetch failed (${error.cause?.code ?? error.message}) for ${urlString}, retrying in ${delay}ms...`);
|
|
1397
1410
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1398
1411
|
continue;
|
|
1399
1412
|
}
|
|
1400
|
-
warn(`Fetch failed for ${urlString}`, error
|
|
1401
|
-
throw error
|
|
1413
|
+
warn(`Fetch failed for ${urlString}`, error);
|
|
1414
|
+
throw error;
|
|
1402
1415
|
}
|
|
1403
1416
|
}
|
|
1404
1417
|
throw new Error(`Max retries exceeded for ${urlString}`);
|
|
@@ -1561,8 +1574,8 @@ function initWatcher(filesDir) {
|
|
|
1561
1574
|
});
|
|
1562
1575
|
debug(`Watching directory: ${filesDir}`);
|
|
1563
1576
|
const emitEvent = async (kind, absolutePath) => {
|
|
1564
|
-
if (!isSupportedExtension(absolutePath)) return;
|
|
1565
|
-
const rawRelativePath = normalizePath(getRelativePath(filesDir, absolutePath));
|
|
1577
|
+
if (!isSupportedExtension$1(absolutePath)) return;
|
|
1578
|
+
const rawRelativePath = normalizePath$1(getRelativePath(filesDir, absolutePath));
|
|
1566
1579
|
const relativePath = sanitizeFilePath(rawRelativePath, false).path;
|
|
1567
1580
|
let effectiveAbsolutePath = absolutePath;
|
|
1568
1581
|
if (relativePath !== rawRelativePath && kind === "add") {
|
|
@@ -1624,7 +1637,7 @@ var FileMetadataCache = class {
|
|
|
1624
1637
|
const loaded = await loadPersistedState(projectDir);
|
|
1625
1638
|
this.persisted = loaded;
|
|
1626
1639
|
this.metadata = /* @__PURE__ */ new Map();
|
|
1627
|
-
for (const [fileName, state] of loaded.entries()) this.metadata.set(fileName, {
|
|
1640
|
+
for (const [fileName, state] of loaded.entries()) this.metadata.set(fileKeyForLookup(fileName), {
|
|
1628
1641
|
localHash: state.contentHash,
|
|
1629
1642
|
lastSyncedHash: state.contentHash,
|
|
1630
1643
|
lastRemoteTimestamp: state.timestamp
|
|
@@ -1632,10 +1645,10 @@ var FileMetadataCache = class {
|
|
|
1632
1645
|
this.initialized = true;
|
|
1633
1646
|
}
|
|
1634
1647
|
get(fileName) {
|
|
1635
|
-
return this.metadata.get(fileName);
|
|
1648
|
+
return this.metadata.get(fileKeyForLookup(fileName));
|
|
1636
1649
|
}
|
|
1637
1650
|
has(fileName) {
|
|
1638
|
-
return this.metadata.has(fileName);
|
|
1651
|
+
return this.metadata.has(fileKeyForLookup(fileName));
|
|
1639
1652
|
}
|
|
1640
1653
|
size() {
|
|
1641
1654
|
return this.metadata.size;
|
|
@@ -1644,33 +1657,36 @@ var FileMetadataCache = class {
|
|
|
1644
1657
|
return this.persisted;
|
|
1645
1658
|
}
|
|
1646
1659
|
recordRemoteWrite(fileName, content, remoteModifiedAt) {
|
|
1660
|
+
const key = fileKeyForLookup(fileName);
|
|
1647
1661
|
const contentHash = hashFileContent(content);
|
|
1648
|
-
this.metadata.set(
|
|
1662
|
+
this.metadata.set(key, {
|
|
1649
1663
|
localHash: contentHash,
|
|
1650
1664
|
lastSyncedHash: contentHash,
|
|
1651
1665
|
lastRemoteTimestamp: remoteModifiedAt
|
|
1652
1666
|
});
|
|
1653
|
-
this.persisted.set(
|
|
1667
|
+
this.persisted.set(key, {
|
|
1654
1668
|
contentHash,
|
|
1655
1669
|
timestamp: remoteModifiedAt
|
|
1656
1670
|
});
|
|
1657
1671
|
this.schedulePersist();
|
|
1658
1672
|
}
|
|
1659
1673
|
recordSyncedSnapshot(fileName, contentHash, remoteModifiedAt) {
|
|
1660
|
-
|
|
1674
|
+
const key = fileKeyForLookup(fileName);
|
|
1675
|
+
this.metadata.set(key, {
|
|
1661
1676
|
localHash: contentHash,
|
|
1662
1677
|
lastSyncedHash: contentHash,
|
|
1663
1678
|
lastRemoteTimestamp: remoteModifiedAt
|
|
1664
1679
|
});
|
|
1665
|
-
this.persisted.set(
|
|
1680
|
+
this.persisted.set(key, {
|
|
1666
1681
|
contentHash,
|
|
1667
1682
|
timestamp: remoteModifiedAt
|
|
1668
1683
|
});
|
|
1669
1684
|
this.schedulePersist();
|
|
1670
1685
|
}
|
|
1671
1686
|
recordDelete(fileName) {
|
|
1672
|
-
|
|
1673
|
-
this.
|
|
1687
|
+
const key = fileKeyForLookup(fileName);
|
|
1688
|
+
this.metadata.delete(key);
|
|
1689
|
+
this.persisted.delete(key);
|
|
1674
1690
|
this.schedulePersist();
|
|
1675
1691
|
}
|
|
1676
1692
|
async flush() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "framer-code-link",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "CLI tool for syncing Framer code components - controller-centric architecture",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@typescript/ata": "^0.9.8",
|
|
26
26
|
"chokidar": "^5.0.0",
|
|
27
|
-
"commander": "^14.0.
|
|
27
|
+
"commander": "^14.0.3",
|
|
28
28
|
"prettier": "^3.7.4",
|
|
29
29
|
"typescript": "^5.9.3",
|
|
30
30
|
"ws": "^8.18.3"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@code-link/shared": "1.0.0",
|
|
34
34
|
"@types/node": "^22.19.2",
|
|
35
35
|
"@types/ws": "^8.18.1",
|
|
36
|
-
"tsdown": "^0.
|
|
36
|
+
"tsdown": "^0.20.1",
|
|
37
37
|
"tsx": "^4.21.0",
|
|
38
38
|
"vitest": "^4.0.15"
|
|
39
39
|
}
|