@hyperframes/studio-server 0.8.113 → 0.8.114
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.js +72 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -165,7 +165,8 @@ import {
|
|
|
165
165
|
statSync as statSync3,
|
|
166
166
|
fstatSync,
|
|
167
167
|
renameSync as renameSync2,
|
|
168
|
-
readdirSync as readdirSync2
|
|
168
|
+
readdirSync as readdirSync2,
|
|
169
|
+
realpathSync
|
|
169
170
|
} from "fs";
|
|
170
171
|
import { resolve as resolve2, dirname as dirname3, join as join5 } from "path";
|
|
171
172
|
|
|
@@ -1179,18 +1180,83 @@ function readableText(file) {
|
|
|
1179
1180
|
throw error;
|
|
1180
1181
|
}
|
|
1181
1182
|
}
|
|
1182
|
-
|
|
1183
|
+
var escapeRegExp = (text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1184
|
+
var SEPARATOR = String.raw`\\{0,2}[\\/]`;
|
|
1185
|
+
var REFERENCE_START = String.raw`(?<![\w./\\+-])`;
|
|
1186
|
+
var FILE_END = String.raw`(?![\w-]|\.\w)`;
|
|
1187
|
+
function referencePattern(oldPath, isDirectory) {
|
|
1188
|
+
const name = oldPath.split("/").map(escapeRegExp).join(SEPARATOR);
|
|
1189
|
+
const end = isDirectory ? `(?=${SEPARATOR})` : FILE_END;
|
|
1190
|
+
return new RegExp(
|
|
1191
|
+
String.raw`${REFERENCE_START}(?<lead>(?:\.{1,2}${SEPARATOR}|${SEPARATOR}){0,4})${name}${end}`,
|
|
1192
|
+
"g"
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
function referenceRewriter(oldPath, newPath, isDirectory, existing = []) {
|
|
1196
|
+
const pattern = referencePattern(oldPath, isDirectory);
|
|
1197
|
+
const around = /* @__PURE__ */ new Map();
|
|
1198
|
+
const afterLengths = /* @__PURE__ */ new Set();
|
|
1199
|
+
let window = 0;
|
|
1200
|
+
for (const path of existing) {
|
|
1201
|
+
if (path.length <= oldPath.length) continue;
|
|
1202
|
+
for (const start of occurrences(path, oldPath)) {
|
|
1203
|
+
const [before, after] = [path.slice(0, start), path.slice(start + oldPath.length)];
|
|
1204
|
+
const befores = around.get(after) ?? around.set(after, /* @__PURE__ */ new Map()).get(after);
|
|
1205
|
+
(befores.get(before.length) ?? befores.set(before.length, /* @__PURE__ */ new Set()).get(before.length)).add(
|
|
1206
|
+
before
|
|
1207
|
+
);
|
|
1208
|
+
afterLengths.add(after.length);
|
|
1209
|
+
window = Math.max(window, path.length * 3);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
const normalized = (text2) => text2.replace(/\\{0,2}[\\/]/g, "/");
|
|
1213
|
+
return (text2) => text2.replace(pattern, (...args) => {
|
|
1214
|
+
const [match, offset] = [args[0], args.at(-3)];
|
|
1215
|
+
const { lead } = args.at(-1);
|
|
1216
|
+
const at = offset + lead.length;
|
|
1217
|
+
const head = normalized(text2.slice(Math.max(0, at - window), at));
|
|
1218
|
+
const tail = normalized(text2.slice(offset + match.length, offset + match.length + window));
|
|
1219
|
+
const inLonger = [...afterLengths].some((after) => {
|
|
1220
|
+
const befores = tail.length >= after && around.get(tail.slice(0, after));
|
|
1221
|
+
return befores && [...befores].some(
|
|
1222
|
+
([length, set]) => head.length >= length && set.has(head.slice(head.length - length))
|
|
1223
|
+
);
|
|
1224
|
+
});
|
|
1225
|
+
return inLonger ? match : `${lead}${newPath}`;
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
function occurrences(text2, part) {
|
|
1229
|
+
const at = [];
|
|
1230
|
+
for (let i = text2.indexOf(part); i >= 0; i = text2.indexOf(part, i + 1)) at.push(i);
|
|
1231
|
+
return at;
|
|
1232
|
+
}
|
|
1233
|
+
function projectPaths(root, dir = root, prefix = "", ancestors = /* @__PURE__ */ new Set()) {
|
|
1234
|
+
return readableEntries(dir).flatMap((entry) => {
|
|
1235
|
+
const [path, full] = [`${prefix}${entry.name}`, join5(dir, entry.name)];
|
|
1236
|
+
const real = entry.name === "node_modules" ? null : directoryReal(entry, full);
|
|
1237
|
+
if (real === null || ancestors.has(real) || !isSafePath(root, full)) return [path];
|
|
1238
|
+
return [path, ...projectPaths(root, full, `${path}/`, /* @__PURE__ */ new Set([...ancestors, real]))];
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
function directoryReal(entry, full) {
|
|
1242
|
+
try {
|
|
1243
|
+
return entry.isDirectory() || entry.isSymbolicLink() && statSync3(full).isDirectory() ? realpathSync(full) : null;
|
|
1244
|
+
} catch {
|
|
1245
|
+
return null;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
function updateReferences(projectDir, oldPath, newPath, isDirectory) {
|
|
1183
1249
|
const textFiles = walkFiles(
|
|
1184
1250
|
projectDir,
|
|
1185
1251
|
(name) => /\.(html|css|js|jsx|ts|tsx|json|mjs|cjs|md|mdx)$/i.test(name)
|
|
1186
1252
|
);
|
|
1253
|
+
const rewrite = referenceRewriter(oldPath, newPath, isDirectory, projectPaths(projectDir));
|
|
1187
1254
|
let updatedCount = 0;
|
|
1188
1255
|
for (const file of textFiles) {
|
|
1189
1256
|
if (!isSafePath(projectDir, file)) continue;
|
|
1190
1257
|
const content = readableText(file);
|
|
1191
1258
|
if (content === null) continue;
|
|
1192
|
-
|
|
1193
|
-
const updated = content.split(oldPath).join(newPath);
|
|
1259
|
+
const updated = rewrite(content);
|
|
1194
1260
|
if (updated !== content) {
|
|
1195
1261
|
replaceFileAtomically2(file, updated, statSync3(file).mode);
|
|
1196
1262
|
updatedCount++;
|
|
@@ -3027,9 +3093,10 @@ function registerFileRoutes(api, adapter) {
|
|
|
3027
3093
|
if (existsSync3(newAbs)) {
|
|
3028
3094
|
return c.json({ error: "already exists" }, 409);
|
|
3029
3095
|
}
|
|
3096
|
+
const isDirectory = statSync3(res.absPath, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
3030
3097
|
ensureDir(res.project.dir, newAbs);
|
|
3031
3098
|
renameSync2(res.absPath, newAbs);
|
|
3032
|
-
const updatedFiles = updateReferences(res.project.dir, res.filePath, body.newPath);
|
|
3099
|
+
const updatedFiles = updateReferences(res.project.dir, res.filePath, body.newPath, isDirectory);
|
|
3033
3100
|
return c.json({ ok: true, path: body.newPath, updatedReferences: updatedFiles });
|
|
3034
3101
|
});
|
|
3035
3102
|
api.post("/projects/:id/duplicate-file", async (c) => {
|