@epic-web/workshop-utils 6.60.0 → 6.61.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/dist/apps.server.d.ts +8 -0
- package/dist/apps.server.js +74 -9
- package/package.json +1 -1
package/dist/apps.server.d.ts
CHANGED
|
@@ -4871,6 +4871,14 @@ export declare function getAppFromFile(filePath: string): Promise<{
|
|
|
4871
4871
|
epicVideoEmbeds?: string[] | undefined;
|
|
4872
4872
|
} | undefined>;
|
|
4873
4873
|
export declare function savePlayground(): Promise<void>;
|
|
4874
|
+
export type SavedPlayground = {
|
|
4875
|
+
id: string;
|
|
4876
|
+
appName: string;
|
|
4877
|
+
createdAt: string;
|
|
4878
|
+
createdAtMs: number;
|
|
4879
|
+
fullPath: string;
|
|
4880
|
+
};
|
|
4881
|
+
export declare function getSavedPlaygrounds(): Promise<Array<SavedPlayground>>;
|
|
4874
4882
|
export declare function setPlayground(srcDir: string, { reset }?: {
|
|
4875
4883
|
reset?: boolean;
|
|
4876
4884
|
}): Promise<void>;
|
package/dist/apps.server.js
CHANGED
|
@@ -16,12 +16,14 @@ import { cachified, exampleAppCache, playgroundAppCache, problemAppCache, soluti
|
|
|
16
16
|
import { compileMdx } from "./compile-mdx.server.js";
|
|
17
17
|
import { getAppConfig, getStackBlitzUrl } from "./config.server.js";
|
|
18
18
|
import { getPreferences } from "./db.server.js";
|
|
19
|
+
import { logger } from "./logger.js";
|
|
19
20
|
import { getDirModifiedTime } from "./modified-time.server.js";
|
|
20
21
|
import { closeProcess, isAppRunning, runAppDev, waitOnApp, } from "./process-manager.server.js";
|
|
21
22
|
import { requestStorageify } from "./request-context.server.js";
|
|
22
23
|
import { getServerTimeHeader, time } from "./timing.server.js";
|
|
23
24
|
import { dayjs } from "./utils.server.js";
|
|
24
25
|
import { getErrorMessage } from "./utils.js";
|
|
26
|
+
const log = logger('epic:apps');
|
|
25
27
|
global.__epicshop_apps_initialized__ ??= false;
|
|
26
28
|
export function setWorkshopRoot(root = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd()) {
|
|
27
29
|
process.env.EPICSHOP_CONTEXT_CWD = root;
|
|
@@ -656,14 +658,26 @@ async function getTestInfo({ fullPath, }) {
|
|
|
656
658
|
}
|
|
657
659
|
// tests are found in the corresponding solution directory
|
|
658
660
|
const testAppFullPath = (await findSolutionDir({ fullPath })) ?? fullPath;
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
661
|
+
try {
|
|
662
|
+
const dirList = await fs.promises.readdir(testAppFullPath);
|
|
663
|
+
const testFiles = dirList.filter((item) => item.includes('.test.'));
|
|
664
|
+
if (testFiles.length) {
|
|
665
|
+
return {
|
|
666
|
+
type: 'browser',
|
|
667
|
+
pathname: `${getPathname(fullPath)}test/`,
|
|
668
|
+
testFiles,
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
// Handle ENOTDIR error (path is a file, not a directory)
|
|
674
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOTDIR') {
|
|
675
|
+
log(`Skipping non-directory path in getTestInfo: ${testAppFullPath}`);
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
// Re-throw other errors
|
|
679
|
+
throw error;
|
|
680
|
+
}
|
|
667
681
|
}
|
|
668
682
|
return { type: 'none' };
|
|
669
683
|
}
|
|
@@ -772,7 +786,18 @@ async function getExampleAppFromPath(fullPath, index, request) {
|
|
|
772
786
|
}
|
|
773
787
|
async function getExampleApps({ timings, request, } = {}) {
|
|
774
788
|
const examplesDir = path.join(getWorkshopRoot(), 'examples');
|
|
775
|
-
|
|
789
|
+
// Filter to only include directories, not files like README.mdx
|
|
790
|
+
const entries = await fs.promises
|
|
791
|
+
.readdir(examplesDir, { withFileTypes: true })
|
|
792
|
+
.catch(() => []);
|
|
793
|
+
const exampleDirs = entries
|
|
794
|
+
.filter((entry) => {
|
|
795
|
+
if (entry.isDirectory())
|
|
796
|
+
return true;
|
|
797
|
+
log(`Skipping non-directory in examples: ${entry.name}`);
|
|
798
|
+
return false;
|
|
799
|
+
})
|
|
800
|
+
.map((entry) => path.join(examplesDir, entry.name));
|
|
776
801
|
const exampleApps = [];
|
|
777
802
|
for (const exampleDir of exampleDirs) {
|
|
778
803
|
const index = exampleDirs.indexOf(exampleDir);
|
|
@@ -1049,6 +1074,46 @@ locally and uncheck "Enable saving playground."
|
|
|
1049
1074
|
}
|
|
1050
1075
|
await fsExtra.copy(playgroundDir, path.join(savedPlaygroundsDir, savedPlaygroundDirName));
|
|
1051
1076
|
}
|
|
1077
|
+
const savedPlaygroundTimestampPattern = /^(\d{4})\.(\d{2})\.(\d{2})_(\d{2})\.(\d{2})\.(\d{2})$/;
|
|
1078
|
+
function parseSavedPlaygroundDirName(dirName) {
|
|
1079
|
+
const parts = dirName.split('_');
|
|
1080
|
+
if (parts.length < 3)
|
|
1081
|
+
return null;
|
|
1082
|
+
const timestampPart = `${parts[0]}_${parts[1]}`;
|
|
1083
|
+
const appName = parts.slice(2).join('_') || dirName;
|
|
1084
|
+
const match = savedPlaygroundTimestampPattern.exec(timestampPart);
|
|
1085
|
+
if (!match)
|
|
1086
|
+
return null;
|
|
1087
|
+
const [, year, month, day, hour, minute, second] = match;
|
|
1088
|
+
const createdAt = new Date(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second));
|
|
1089
|
+
if (Number.isNaN(createdAt.getTime()))
|
|
1090
|
+
return null;
|
|
1091
|
+
return { appName, createdAt };
|
|
1092
|
+
}
|
|
1093
|
+
export async function getSavedPlaygrounds() {
|
|
1094
|
+
const savedPlaygroundsDir = path.join(getWorkshopRoot(), 'saved-playgrounds');
|
|
1095
|
+
if (!(await exists(savedPlaygroundsDir)))
|
|
1096
|
+
return [];
|
|
1097
|
+
const dirEntries = await fsExtra.readdir(savedPlaygroundsDir, {
|
|
1098
|
+
withFileTypes: true,
|
|
1099
|
+
});
|
|
1100
|
+
const savedPlaygrounds = await Promise.all(dirEntries
|
|
1101
|
+
.filter((entry) => entry.isDirectory())
|
|
1102
|
+
.map(async (entry) => {
|
|
1103
|
+
const fullPath = path.join(savedPlaygroundsDir, entry.name);
|
|
1104
|
+
const parsed = parseSavedPlaygroundDirName(entry.name);
|
|
1105
|
+
const stat = await fsExtra.stat(fullPath).catch(() => null);
|
|
1106
|
+
const createdAt = parsed?.createdAt ?? (stat ? new Date(stat.mtimeMs) : new Date(0));
|
|
1107
|
+
return {
|
|
1108
|
+
id: entry.name,
|
|
1109
|
+
appName: parsed?.appName ?? entry.name,
|
|
1110
|
+
createdAt: createdAt.toISOString(),
|
|
1111
|
+
createdAtMs: createdAt.getTime(),
|
|
1112
|
+
fullPath,
|
|
1113
|
+
};
|
|
1114
|
+
}));
|
|
1115
|
+
return savedPlaygrounds.sort((a, b) => b.createdAtMs - a.createdAtMs);
|
|
1116
|
+
}
|
|
1052
1117
|
export async function setPlayground(srcDir, { reset } = {}) {
|
|
1053
1118
|
const preferences = await getPreferences();
|
|
1054
1119
|
const playgroundApp = await getAppByName('playground');
|