@epic-web/workshop-utils 5.0.1 → 5.0.3

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.
@@ -2,6 +2,9 @@ import { type CacheEntry } from '@epic-web/cachified';
2
2
  import '@total-typescript/ts-reset';
3
3
  import { z } from 'zod';
4
4
  import { type Timings } from './timing.server.js';
5
+ declare global {
6
+ var __epicshop_apps_initialized__: boolean | undefined;
7
+ }
5
8
  export declare const workshopRoot: string;
6
9
  type CachifiedOptions = {
7
10
  timings?: Timings;
@@ -3138,8 +3141,8 @@ export declare function isPlaygroundApp(app: any): app is PlaygroundApp;
3138
3141
  export declare function isExampleApp(app: any): app is ExampleApp;
3139
3142
  export declare function isExerciseStepApp(app: any): app is ExerciseStepApp;
3140
3143
  export declare const modifiedTimes: Map<string, number>;
3141
- export declare function init(): void;
3142
- export declare function setModifiedTimesForDir(dir: string): void;
3144
+ export declare function init(): Promise<void>;
3145
+ export declare function setModifiedTimesForAppDirs(...filePaths: Array<string>): void;
3143
3146
  export declare function getForceFreshForDir(cacheEntry: CacheEntry | null | undefined, ...dirs: Array<string | undefined | null>): true | undefined;
3144
3147
  export declare function getExercises({ timings, request, }?: CachifiedOptions): Promise<Array<Exercise>>;
3145
3148
  export declare function getApps({ timings, request, forceFresh, }?: CachifiedOptions & {
@@ -4179,5 +4182,9 @@ export declare function getWorkshopFinished({ request, }?: {
4179
4182
  readonly relativePath: "exercises/finished.mdx";
4180
4183
  }>;
4181
4184
  export declare function getRelativePath(filePath: string): string;
4185
+ /**
4186
+ * Given a file path, this will determine the path to the app that file belongs to.
4187
+ */
4188
+ export declare function getAppPathFromFilePath(filePath: string): string | null;
4182
4189
  export {};
4183
4190
  //# sourceMappingURL=apps.server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apps.server.d.ts","sourceRoot":"","sources":["../../src/apps.server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apps.server.d.ts","sourceRoot":"","sources":["../../src/apps.server.ts"],"names":[],"mappings":"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"}
@@ -1,22 +1,23 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { remember } from '@epic-web/remember';
4
+ import chokidar from 'chokidar';
4
5
  /// TODO: figure out why this import is necessary (without it tsc seems to not honor the boolean reset 🤷‍♂️)
5
6
  import '@total-typescript/ts-reset';
7
+ import closeWithGrace from 'close-with-grace';
6
8
  import { execa } from 'execa';
7
9
  import fsExtra from 'fs-extra';
8
- import { glob } from 'glob';
9
10
  import { globby, isGitIgnored } from 'globby';
10
11
  import { z } from 'zod';
11
12
  import { appsCache, cachified, exampleAppCache, playgroundAppCache, problemAppCache, solutionAppCache, } from './cache.server.js';
12
- import { getWatcher, withoutWatcher } from './change-tracker.server.js';
13
13
  import { compileMdx } from './compile-mdx.server.js';
14
- import { bustWorkshopConfigCache, getAppConfig, getStackBlitzUrl, getWorkshopConfig, } from './config.server.js';
14
+ import { getAppConfig, getStackBlitzUrl, getWorkshopConfig, } from './config.server.js';
15
15
  import { getEnv, init as initEnv } from './env.server.js';
16
+ import { getDirModifiedTime } from './modified-time.server.js';
16
17
  import { closeProcess, isAppRunning, runAppDev, waitOnApp, } from './process-manager.server.js';
17
- import { getServerTimeHeader } from './timing.server.js';
18
+ import { getServerTimeHeader, time } from './timing.server.js';
18
19
  import { getErrorMessage } from './utils.js';
19
- let initialized = false;
20
+ global.__epicshop_apps_initialized__ ??= false;
20
21
  export const workshopRoot = (process.env.EPICSHOP_CONTEXT_CWD =
21
22
  process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd());
22
23
  const playgroundAppNameInfoPath = path.join(workshopRoot, 'node_modules', '.cache', 'epicshop', 'playground.json');
@@ -141,27 +142,54 @@ async function firstToExist(...files) {
141
142
  return index === -1 ? null : files[index];
142
143
  }
143
144
  export const modifiedTimes = remember('modified_times', () => new Map());
144
- export function init() {
145
- if (initialized)
145
+ export async function init() {
146
+ if (global.__epicshop_apps_initialized__)
146
147
  return;
147
- initialized = true;
148
+ global.__epicshop_apps_initialized__ = true;
148
149
  const config = getWorkshopConfig();
149
150
  process.env.EPICSHOP_GITHUB_REPO = config.githubRepo;
150
151
  process.env.EPICSHOP_GITHUB_ROOT = config.githubRoot;
151
152
  initEnv();
152
153
  global.ENV = getEnv();
153
- getWatcher()?.on('all', handleFileChanges);
154
- }
155
- async function handleFileChanges(event, filePath) {
156
- if (filePath === path.join(workshopRoot, 'package.json')) {
157
- bustWorkshopConfigCache();
158
- }
159
- const apps = await getApps();
160
- for (const app of apps) {
161
- if (filePath.startsWith(app.fullPath)) {
162
- modifiedTimes.set(app.fullPath, Date.now());
163
- break;
164
- }
154
+ if (!ENV.EPICSHOP_DEPLOYED &&
155
+ process.env.EPICSHOP_ENABLE_WATCHER === 'true') {
156
+ const isIgnored = await isGitIgnored({ cwd: workshopRoot });
157
+ // watch the README, FINISHED, and package.json for changes that affect the apps
158
+ const filesToWatch = ['README.mdx', 'FINISHED.mdx', 'package.json'];
159
+ const chok = chokidar.watch(['examples', 'playground', 'exercises'], {
160
+ cwd: workshopRoot,
161
+ ignoreInitial: true,
162
+ ignored(filePath, stats) {
163
+ if (isIgnored(filePath))
164
+ return true;
165
+ if (filePath.includes('.git'))
166
+ return true;
167
+ if (stats?.isDirectory()) {
168
+ if (filePath.endsWith('playground'))
169
+ return false;
170
+ const pathParts = filePath.split(path.sep);
171
+ if (pathParts.at(-2) === 'examples')
172
+ return false;
173
+ // steps
174
+ if (pathParts.at(-3) === 'exercises')
175
+ return false;
176
+ // exercises
177
+ if (pathParts.at(-2) === 'exercises')
178
+ return false;
179
+ // the exercise dir itself
180
+ if (pathParts.at(-1) === 'exercises')
181
+ return false;
182
+ return true;
183
+ }
184
+ return stats?.isFile()
185
+ ? !filesToWatch.some((file) => filePath.endsWith(file))
186
+ : false;
187
+ },
188
+ });
189
+ chok.on('all', (_event, filePath) => {
190
+ setModifiedTimesForAppDirs(path.join(workshopRoot, filePath));
191
+ });
192
+ closeWithGrace(() => chok.close());
165
193
  }
166
194
  }
167
195
  function getForceFresh(cacheEntry) {
@@ -172,8 +200,17 @@ function getForceFresh(cacheEntry) {
172
200
  return undefined;
173
201
  return latestModifiedTime > cacheEntry.metadata.createdTime ? true : undefined;
174
202
  }
175
- export function setModifiedTimesForDir(dir) {
176
- modifiedTimes.set(dir, Date.now());
203
+ export function setModifiedTimesForAppDirs(...filePaths) {
204
+ const now = Date.now();
205
+ for (const filePath of filePaths) {
206
+ const appDir = getAppPathFromFilePath(filePath);
207
+ if (appDir) {
208
+ modifiedTimes.set(appDir, now);
209
+ }
210
+ else {
211
+ console.warn(`filePath ${filePath} does not match any app dir`);
212
+ }
213
+ }
177
214
  }
178
215
  export function getForceFreshForDir(cacheEntry, ...dirs) {
179
216
  const truthyDirs = dirs.filter(Boolean);
@@ -275,8 +312,7 @@ export async function getExercises({ timings, request, } = {}) {
275
312
  }
276
313
  let appCallCount = 0;
277
314
  export async function getApps({ timings, request, forceFresh, } = {}) {
278
- if (!initialized)
279
- init();
315
+ await init();
280
316
  const key = 'apps';
281
317
  const apps = await cachified({
282
318
  key,
@@ -289,10 +325,24 @@ export async function getApps({ timings, request, forceFresh, } = {}) {
289
325
  ttl: 1000 * 60 * 60 * 24,
290
326
  forceFresh: forceFresh ?? getForceFresh(appsCache.get(key)),
291
327
  getFreshValue: async () => {
292
- const playgroundApp = await getPlaygroundApp({ request, timings });
293
- const problemApps = await getProblemApps({ request, timings });
294
- const solutionApps = await getSolutionApps({ request, timings });
295
- const exampleApps = await getExampleApps({ request, timings });
328
+ const [playgroundApp, problemApps, solutionApps, exampleApps] = await Promise.all([
329
+ time(() => getPlaygroundApp({ request, timings }), {
330
+ type: 'getPlaygroundApp',
331
+ timings,
332
+ }),
333
+ time(() => getProblemApps({ request, timings }), {
334
+ type: 'getProblemApps',
335
+ timings,
336
+ }),
337
+ time(() => getSolutionApps({ request, timings }), {
338
+ type: 'getSolutionApps',
339
+ timings,
340
+ }),
341
+ time(() => getExampleApps({ request, timings }), {
342
+ type: 'getExampleApps',
343
+ timings,
344
+ }),
345
+ ]);
296
346
  const sortedApps = [
297
347
  playgroundApp,
298
348
  ...problemApps,
@@ -389,18 +439,38 @@ export function extractNumbersAndTypeFromAppNameOrPath(fullPathOrAppName) {
389
439
  }
390
440
  async function getProblemDirs() {
391
441
  const exercisesDir = path.join(workshopRoot, 'exercises');
392
- const problemDirs = (await glob('**/*.problem*', {
393
- cwd: exercisesDir,
394
- ignore: 'node_modules/**',
395
- })).map((p) => path.join(exercisesDir, p));
442
+ const problemDirs = [];
443
+ const exerciseSubDirs = await readDir(exercisesDir);
444
+ for (const subDir of exerciseSubDirs) {
445
+ const fullSubDir = path.join(exercisesDir, subDir);
446
+ // catch handles non-directories without us having to bother checking
447
+ // whether it's a directory
448
+ const subDirContents = await readDir(fullSubDir).catch(() => null);
449
+ if (!subDirContents)
450
+ continue;
451
+ const problemSubDirs = subDirContents
452
+ .filter((dir) => dir.includes('.problem'))
453
+ .map((dir) => path.join(fullSubDir, dir));
454
+ problemDirs.push(...problemSubDirs);
455
+ }
396
456
  return problemDirs;
397
457
  }
398
458
  async function getSolutionDirs() {
399
459
  const exercisesDir = path.join(workshopRoot, 'exercises');
400
- const solutionDirs = (await glob('**/*.solution*', {
401
- cwd: exercisesDir,
402
- ignore: 'node_modules/**',
403
- })).map((p) => path.join(exercisesDir, p));
460
+ const solutionDirs = [];
461
+ const exerciseSubDirs = await readDir(exercisesDir);
462
+ for (const subDir of exerciseSubDirs) {
463
+ const fullSubDir = path.join(exercisesDir, subDir);
464
+ // catch handles non-directories without us having to bother checking
465
+ // whether it's a directory
466
+ const subDirContents = await readDir(fullSubDir).catch(() => null);
467
+ if (!subDirContents)
468
+ continue;
469
+ const solutionSubDirs = subDirContents
470
+ .filter((dir) => dir.includes('.solution'))
471
+ .map((dir) => path.join(fullSubDir, dir));
472
+ solutionDirs.push(...solutionSubDirs);
473
+ }
404
474
  return solutionDirs;
405
475
  }
406
476
  /**
@@ -622,7 +692,7 @@ async function getExampleAppFromPath(fullPath, index, request) {
622
692
  }
623
693
  async function getExampleApps({ timings, request, } = {}) {
624
694
  const examplesDir = path.join(workshopRoot, 'examples');
625
- const exampleDirs = (await glob('*', { cwd: examplesDir, ignore: 'node_modules/**' })).map((p) => path.join(examplesDir, p));
695
+ const exampleDirs = (await readDir(examplesDir)).map((p) => path.join(examplesDir, p));
626
696
  const exampleApps = [];
627
697
  for (const exampleDir of exampleDirs) {
628
698
  const index = exampleDirs.indexOf(exampleDir);
@@ -635,10 +705,12 @@ async function getExampleApps({ timings, request, } = {}) {
635
705
  timingKey: exampleDir.replace(`${examplesDir}${path.sep}`, ''),
636
706
  request,
637
707
  forceFresh: getForceFreshForDir(exampleAppCache.get(key), exampleDir),
638
- getFreshValue: () => getExampleAppFromPath(exampleDir, index, request).catch((error) => {
639
- console.error(error);
640
- return null;
641
- }),
708
+ getFreshValue: async () => {
709
+ return getExampleAppFromPath(exampleDir, index, request).catch((error) => {
710
+ console.error(error);
711
+ return null;
712
+ });
713
+ },
642
714
  });
643
715
  if (exampleApp)
644
716
  exampleApps.push(exampleApp);
@@ -701,10 +773,12 @@ async function getSolutionApps({ timings, request, } = {}) {
701
773
  request,
702
774
  ttl: 1000 * 60 * 60 * 24,
703
775
  forceFresh: getForceFreshForDir(solutionAppCache.get(solutionDir), solutionDir),
704
- getFreshValue: () => getSolutionAppFromPath(solutionDir, request).catch((error) => {
705
- console.error(error);
706
- return null;
707
- }),
776
+ getFreshValue: async () => {
777
+ return getSolutionAppFromPath(solutionDir, request).catch((error) => {
778
+ console.error(error);
779
+ return null;
780
+ });
781
+ },
708
782
  });
709
783
  if (solutionApp)
710
784
  solutionApps.push(solutionApp);
@@ -768,10 +842,12 @@ async function getProblemApps({ timings, request, } = {}) {
768
842
  request,
769
843
  ttl: 1000 * 60 * 60 * 24,
770
844
  forceFresh: getForceFreshForDir(problemAppCache.get(problemDir), problemDir, solutionDir),
771
- getFreshValue: () => getProblemAppFromPath(problemDir).catch((error) => {
772
- console.error(error);
773
- return null;
774
- }),
845
+ getFreshValue: async () => {
846
+ return getProblemAppFromPath(problemDir).catch((error) => {
847
+ console.error(error);
848
+ return null;
849
+ });
850
+ },
775
851
  });
776
852
  if (problemApp)
777
853
  problemApps.push(problemApp);
@@ -868,125 +944,121 @@ export async function getAppFromFile(filePath) {
868
944
  }
869
945
  export async function setPlayground(srcDir, { reset } = {}) {
870
946
  const destDir = path.join(workshopRoot, 'playground');
871
- await withoutWatcher(async () => {
872
- const isIgnored = await isGitIgnored({ cwd: srcDir });
873
- const playgroundApp = await getAppByName('playground');
874
- const playgroundWasRunning = playgroundApp
875
- ? isAppRunning(playgroundApp)
876
- : false;
877
- if (playgroundApp && reset) {
878
- await closeProcess(playgroundApp.name);
879
- await fsExtra.remove(destDir);
880
- }
881
- const setPlaygroundTimestamp = Date.now();
882
- // run prepare-playground script if it exists
883
- const preSetPlaygroundPath = await firstToExist(path.join(srcDir, 'epicshop', 'pre-set-playground.js'), path.join(workshopRoot, 'epicshop', 'pre-set-playground.js'));
884
- if (preSetPlaygroundPath) {
885
- await execa('node', [preSetPlaygroundPath], {
886
- cwd: workshopRoot,
887
- stdio: 'inherit',
888
- env: {
889
- EPICSHOP_PLAYGROUND_TIMESTAMP: setPlaygroundTimestamp.toString(),
890
- EPICSHOP_PLAYGROUND_DEST_DIR: destDir,
891
- EPICSHOP_PLAYGROUND_SRC_DIR: srcDir,
892
- EPICSHOP_PLAYGROUND_WAS_RUNNING: playgroundWasRunning.toString(),
893
- },
894
- });
895
- }
896
- const basename = path.basename(srcDir);
897
- // If we don't delete the destination node_modules first then copying the new
898
- // node_modules has issues.
899
- await fsExtra.remove(path.join(destDir, 'node_modules'));
900
- // Copy the contents of the source directory to the destination directory recursively
901
- await fsExtra.copy(srcDir, destDir, {
902
- filter: async (srcFile, destFile) => {
903
- if (srcFile.includes(`${basename}${path.sep}build`) ||
904
- srcFile.includes(`${basename}${path.sep}public${path.sep}build`)) {
905
- return false;
906
- }
907
- if (srcFile === srcDir)
908
- return true;
909
- // we copy node_modules even though it's .gitignored
910
- if (srcFile.includes('node_modules'))
947
+ const isIgnored = await isGitIgnored({ cwd: srcDir });
948
+ const playgroundApp = await getAppByName('playground');
949
+ const playgroundWasRunning = playgroundApp
950
+ ? isAppRunning(playgroundApp)
951
+ : false;
952
+ if (playgroundApp && reset) {
953
+ await closeProcess(playgroundApp.name);
954
+ await fsExtra.remove(destDir);
955
+ }
956
+ const setPlaygroundTimestamp = Date.now();
957
+ // run prepare-playground script if it exists
958
+ const preSetPlaygroundPath = await firstToExist(path.join(srcDir, 'epicshop', 'pre-set-playground.js'), path.join(workshopRoot, 'epicshop', 'pre-set-playground.js'));
959
+ if (preSetPlaygroundPath) {
960
+ await execa('node', [preSetPlaygroundPath], {
961
+ cwd: workshopRoot,
962
+ stdio: 'inherit',
963
+ env: {
964
+ EPICSHOP_PLAYGROUND_TIMESTAMP: setPlaygroundTimestamp.toString(),
965
+ EPICSHOP_PLAYGROUND_DEST_DIR: destDir,
966
+ EPICSHOP_PLAYGROUND_SRC_DIR: srcDir,
967
+ EPICSHOP_PLAYGROUND_WAS_RUNNING: playgroundWasRunning.toString(),
968
+ },
969
+ });
970
+ }
971
+ const basename = path.basename(srcDir);
972
+ // If we don't delete the destination node_modules first then copying the new
973
+ // node_modules has issues.
974
+ await fsExtra.remove(path.join(destDir, 'node_modules'));
975
+ // Copy the contents of the source directory to the destination directory recursively
976
+ await fsExtra.copy(srcDir, destDir, {
977
+ filter: async (srcFile, destFile) => {
978
+ if (srcFile.includes(`${basename}${path.sep}build`) ||
979
+ srcFile.includes(`${basename}${path.sep}public${path.sep}build`)) {
980
+ return false;
981
+ }
982
+ if (srcFile === srcDir)
983
+ return true;
984
+ // we copy node_modules even though it's .gitignored
985
+ if (srcFile.includes('node_modules'))
986
+ return true;
987
+ // make sure .env is copied whether it's .gitignored or not
988
+ if (srcFile.endsWith('.env'))
989
+ return true;
990
+ if (isIgnored(srcFile))
991
+ return false;
992
+ try {
993
+ const isDir = (await fsExtra.stat(srcFile)).isDirectory();
994
+ if (isDir)
911
995
  return true;
912
- // make sure .env is copied whether it's .gitignored or not
913
- if (srcFile.endsWith('.env'))
996
+ const destIsDir = (await fsExtra.stat(destFile)).isDirectory();
997
+ // weird, but ok
998
+ if (destIsDir)
914
999
  return true;
915
- if (isIgnored(srcFile))
1000
+ // it's better to check if the contents are the same before copying
1001
+ // because it avoids unnecessary writes and reduces the impact on any
1002
+ // file watchers (like the remix dev server). In practice, it's definitely
1003
+ // slower, but it's better because it doesn't cause the dev server to
1004
+ // crash as often.
1005
+ const currentContents = await fsExtra.readFile(destFile);
1006
+ const newContents = await fsExtra.readFile(srcFile);
1007
+ if (currentContents.equals(newContents))
916
1008
  return false;
917
- try {
918
- const isDir = (await fsExtra.stat(srcFile)).isDirectory();
919
- if (isDir)
920
- return true;
921
- const destIsDir = (await fsExtra.stat(destFile)).isDirectory();
922
- // weird, but ok
923
- if (destIsDir)
924
- return true;
925
- // it's better to check if the contents are the same before copying
926
- // because it avoids unnecessary writes and reduces the impact on any
927
- // file watchers (like the remix dev server). In practice, it's definitely
928
- // slower, but it's better because it doesn't cause the dev server to
929
- // crash as often.
930
- const currentContents = await fsExtra.readFile(destFile);
931
- const newContents = await fsExtra.readFile(srcFile);
932
- if (currentContents.equals(newContents))
933
- return false;
934
- return true;
935
- }
936
- catch {
937
- // 🤷‍♂️ should probably copy it in this case
938
- return true;
939
- }
1009
+ return true;
1010
+ }
1011
+ catch {
1012
+ // 🤷‍♂️ should probably copy it in this case
1013
+ return true;
1014
+ }
1015
+ },
1016
+ });
1017
+ async function getFiles(dir) {
1018
+ // make globby friendly to windows
1019
+ const dirPath = dir.replace(/\\/g, '/');
1020
+ const files = await globby([`${dirPath}/**/*`, '!**/build/**/*'], {
1021
+ onlyFiles: false,
1022
+ dot: true,
1023
+ });
1024
+ return files.map((f) => f.replace(dirPath, ''));
1025
+ }
1026
+ // Remove files from destDir that were in destDir before but are not in srcDir
1027
+ const srcFiles = await getFiles(srcDir);
1028
+ const destFiles = await getFiles(destDir);
1029
+ const filesToDelete = destFiles.filter((fileName) => !srcFiles.includes(fileName));
1030
+ for (const fileToDelete of filesToDelete) {
1031
+ await fsExtra.remove(path.join(destDir, fileToDelete));
1032
+ }
1033
+ const appName = getAppName(srcDir);
1034
+ await fsExtra.ensureDir(path.dirname(playgroundAppNameInfoPath));
1035
+ await fsExtra.writeJSON(playgroundAppNameInfoPath, { appName });
1036
+ const playgroundIsStillRunning = playgroundApp
1037
+ ? isAppRunning(playgroundApp)
1038
+ : false;
1039
+ const restartPlayground = playgroundWasRunning && !playgroundIsStillRunning;
1040
+ // run postSet-playground script if it exists
1041
+ const postSetPlaygroundPath = await firstToExist(path.join(srcDir, 'epicshop', 'post-set-playground.js'), path.join(workshopRoot, 'epicshop', 'post-set-playground.js'));
1042
+ if (postSetPlaygroundPath) {
1043
+ await execa('node', [postSetPlaygroundPath], {
1044
+ cwd: workshopRoot,
1045
+ stdio: 'inherit',
1046
+ env: {
1047
+ EPICSHOP_PLAYGROUND_TIMESTAMP: setPlaygroundTimestamp.toString(),
1048
+ EPICSHOP_PLAYGROUND_SRC_DIR: srcDir,
1049
+ EPICSHOP_PLAYGROUND_DEST_DIR: destDir,
1050
+ EPICSHOP_PLAYGROUND_WAS_RUNNING: playgroundWasRunning.toString(),
1051
+ EPICSHOP_PLAYGROUND_IS_STILL_RUNNING: playgroundIsStillRunning.toString(),
1052
+ EPICSHOP_PLAYGROUND_RESTART_PLAYGROUND: restartPlayground.toString(),
940
1053
  },
941
1054
  });
942
- async function getFiles(dir) {
943
- // make globby friendly to windows
944
- const dirPath = dir.replace(/\\/g, '/');
945
- const files = await globby([`${dirPath}/**/*`, '!**/build/**/*'], {
946
- onlyFiles: false,
947
- dot: true,
948
- });
949
- return files.map((f) => f.replace(dirPath, ''));
950
- }
951
- // Remove files from destDir that were in destDir before but are not in srcDir
952
- const srcFiles = await getFiles(srcDir);
953
- const destFiles = await getFiles(destDir);
954
- const filesToDelete = destFiles.filter((fileName) => !srcFiles.includes(fileName));
955
- for (const fileToDelete of filesToDelete) {
956
- await fsExtra.remove(path.join(destDir, fileToDelete));
957
- }
958
- const appName = getAppName(srcDir);
959
- await fsExtra.ensureDir(path.dirname(playgroundAppNameInfoPath));
960
- await fsExtra.writeJSON(playgroundAppNameInfoPath, { appName });
961
- const playgroundIsStillRunning = playgroundApp
962
- ? isAppRunning(playgroundApp)
963
- : false;
964
- const restartPlayground = playgroundWasRunning && !playgroundIsStillRunning;
965
- // run postSet-playground script if it exists
966
- const postSetPlaygroundPath = await firstToExist(path.join(srcDir, 'epicshop', 'post-set-playground.js'), path.join(workshopRoot, 'epicshop', 'post-set-playground.js'));
967
- if (postSetPlaygroundPath) {
968
- await execa('node', [postSetPlaygroundPath], {
969
- cwd: workshopRoot,
970
- stdio: 'inherit',
971
- env: {
972
- EPICSHOP_PLAYGROUND_TIMESTAMP: setPlaygroundTimestamp.toString(),
973
- EPICSHOP_PLAYGROUND_SRC_DIR: srcDir,
974
- EPICSHOP_PLAYGROUND_DEST_DIR: destDir,
975
- EPICSHOP_PLAYGROUND_WAS_RUNNING: playgroundWasRunning.toString(),
976
- EPICSHOP_PLAYGROUND_IS_STILL_RUNNING: playgroundIsStillRunning.toString(),
977
- EPICSHOP_PLAYGROUND_RESTART_PLAYGROUND: restartPlayground.toString(),
978
- },
979
- });
980
- }
981
- // since we are running without the watcher we need to set the modified time
982
- modifiedTimes.set(destDir, Date.now());
983
- if (playgroundApp && restartPlayground) {
984
- await runAppDev(playgroundApp);
985
- await waitOnApp(playgroundApp);
986
- }
987
- });
988
- // let the app know the playground changed
989
- getWatcher()?.emit('all', 'playground', destDir);
1055
+ }
1056
+ // since we are running without the watcher we need to set the modified time
1057
+ modifiedTimes.set(destDir, Date.now());
1058
+ if (playgroundApp && restartPlayground) {
1059
+ await runAppDev(playgroundApp);
1060
+ await waitOnApp(playgroundApp);
1061
+ }
990
1062
  }
991
1063
  /**
992
1064
  * The playground is based on another app. This returns the app the playground
@@ -1007,31 +1079,6 @@ export async function getPlaygroundAppName() {
1007
1079
  return null;
1008
1080
  }
1009
1081
  }
1010
- async function getDirModifiedTime(dir) {
1011
- // we can't use modifiedTimes because it only stores the modified times of
1012
- // things the app started.
1013
- const isIgnored = await isGitIgnored({ cwd: dir });
1014
- const files = await fs.promises.readdir(dir, { withFileTypes: true });
1015
- const modifiedTimes = await Promise.all(files.map(async (file) => {
1016
- if (isIgnored(file.name))
1017
- return 0;
1018
- const filePath = path.join(dir, file.name);
1019
- if (file.isDirectory()) {
1020
- return getDirModifiedTime(filePath);
1021
- }
1022
- else {
1023
- try {
1024
- const { mtimeMs } = await fs.promises.stat(filePath);
1025
- return mtimeMs;
1026
- }
1027
- catch {
1028
- // Handle errors (e.g., file access permissions, file has been moved or deleted)
1029
- return 0;
1030
- }
1031
- }
1032
- }));
1033
- return Math.max(0, ...modifiedTimes); // Ensure there is a default of 0 if all files are ignored
1034
- }
1035
1082
  export function getAppDisplayName(a, allApps) {
1036
1083
  let displayName = `${a.title} (${a.type})`;
1037
1084
  if (isExerciseStepApp(a)) {
@@ -1081,4 +1128,30 @@ export function getRelativePath(filePath) {
1081
1128
  .replace(playgroundPath, `playground${path.sep}`)
1082
1129
  .replace(exercisesPath, '');
1083
1130
  }
1131
+ /**
1132
+ * Given a file path, this will determine the path to the app that file belongs to.
1133
+ */
1134
+ export function getAppPathFromFilePath(filePath) {
1135
+ const [, withinWorkshopRootHalf] = filePath.split(workshopRoot);
1136
+ if (!withinWorkshopRootHalf) {
1137
+ return null;
1138
+ }
1139
+ const [part1, part2, part3] = withinWorkshopRootHalf
1140
+ .split(path.sep)
1141
+ .filter(Boolean);
1142
+ // Check if the file is in the playground
1143
+ if (part1 === 'playground') {
1144
+ return path.join(workshopRoot, 'playground');
1145
+ }
1146
+ // Check if the file is in an example
1147
+ if (part1 === 'examples' && part2) {
1148
+ return path.join(workshopRoot, 'examples', part2);
1149
+ }
1150
+ // Check if the file is in an exercise
1151
+ if (part1 === 'exercises' && part2 && part3) {
1152
+ return path.join(workshopRoot, 'exercises', part2, part3);
1153
+ }
1154
+ // If we couldn't determine the app path, return null
1155
+ return null;
1156
+ }
1084
1157
  //# sourceMappingURL=apps.server.js.map