@epic-web/workshop-utils 6.61.4 → 6.62.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.
@@ -472,7 +472,7 @@ declare const SolutionAppSchema: z.ZodObject<{
472
472
  instructionsCode?: string | undefined;
473
473
  epicVideoEmbeds?: string[] | undefined;
474
474
  }>;
475
- declare const ExampleAppSchema: z.ZodObject<{
475
+ declare const ExtraAppSchema: z.ZodObject<{
476
476
  /** a unique identifier for the app */
477
477
  name: z.ZodString;
478
478
  /** the title of the app used for display (comes from the package.json title prop) */
@@ -550,7 +550,7 @@ declare const ExampleAppSchema: z.ZodObject<{
550
550
  }>]>;
551
551
  stackBlitzUrl: z.ZodNullable<z.ZodString>;
552
552
  } & {
553
- type: z.ZodLiteral<"example">;
553
+ type: z.ZodLiteral<"extra">;
554
554
  }, "strip", z.ZodTypeAny, {
555
555
  test: {
556
556
  type: "browser";
@@ -562,7 +562,7 @@ declare const ExampleAppSchema: z.ZodObject<{
562
562
  } | {
563
563
  type: "none";
564
564
  };
565
- type: "example";
565
+ type: "extra";
566
566
  title: string;
567
567
  name: string;
568
568
  fullPath: string;
@@ -595,7 +595,7 @@ declare const ExampleAppSchema: z.ZodObject<{
595
595
  } | {
596
596
  type: "none";
597
597
  };
598
- type: "example";
598
+ type: "extra";
599
599
  title: string;
600
600
  name: string;
601
601
  fullPath: string;
@@ -3683,7 +3683,7 @@ declare const AppSchema: z.ZodUnion<[z.ZodUnion<[z.ZodObject<{
3683
3683
  }>]>;
3684
3684
  stackBlitzUrl: z.ZodNullable<z.ZodString>;
3685
3685
  } & {
3686
- type: z.ZodLiteral<"example">;
3686
+ type: z.ZodLiteral<"extra">;
3687
3687
  }, "strip", z.ZodTypeAny, {
3688
3688
  test: {
3689
3689
  type: "browser";
@@ -3695,7 +3695,7 @@ declare const AppSchema: z.ZodUnion<[z.ZodUnion<[z.ZodObject<{
3695
3695
  } | {
3696
3696
  type: "none";
3697
3697
  };
3698
- type: "example";
3698
+ type: "extra";
3699
3699
  title: string;
3700
3700
  name: string;
3701
3701
  fullPath: string;
@@ -3728,7 +3728,7 @@ declare const AppSchema: z.ZodUnion<[z.ZodUnion<[z.ZodObject<{
3728
3728
  } | {
3729
3729
  type: "none";
3730
3730
  };
3731
- type: "example";
3731
+ type: "extra";
3732
3732
  title: string;
3733
3733
  name: string;
3734
3734
  fullPath: string;
@@ -3754,7 +3754,7 @@ declare const AppSchema: z.ZodUnion<[z.ZodUnion<[z.ZodObject<{
3754
3754
  export type BaseExerciseStepApp = z.infer<typeof BaseExerciseStepAppSchema>;
3755
3755
  export type ProblemApp = z.infer<typeof ProblemAppSchema>;
3756
3756
  export type SolutionApp = z.infer<typeof SolutionAppSchema>;
3757
- export type ExampleApp = z.infer<typeof ExampleAppSchema>;
3757
+ export type ExtraApp = z.infer<typeof ExtraAppSchema>;
3758
3758
  export type PlaygroundApp = z.infer<typeof PlaygroundAppSchema>;
3759
3759
  export type ExerciseStepApp = z.infer<typeof ExerciseStepAppSchema>;
3760
3760
  export type App = z.infer<typeof AppSchema>;
@@ -3770,7 +3770,7 @@ export declare function isFirstStepSolutionApp(app: App): app is SolutionApp & {
3770
3770
  stepNumber: 1;
3771
3771
  };
3772
3772
  export declare function isPlaygroundApp(app: any): app is PlaygroundApp;
3773
- export declare function isExampleApp(app: any): app is ExampleApp;
3773
+ export declare function isExtraApp(app: any): app is ExtraApp;
3774
3774
  export declare function isExerciseStepApp(app: any): app is ExerciseStepApp;
3775
3775
  export declare const modifiedTimes: Map<string, number>;
3776
3776
  export declare function init(workshopRoot?: string): Promise<void>;
@@ -4518,7 +4518,7 @@ export declare function getAppByName(name: string, { request, timings }?: Cachif
4518
4518
  } | {
4519
4519
  type: "none";
4520
4520
  };
4521
- type: "example";
4521
+ type: "extra";
4522
4522
  title: string;
4523
4523
  name: string;
4524
4524
  fullPath: string;
@@ -4812,7 +4812,7 @@ export declare function getAppFromFile(filePath: string): Promise<{
4812
4812
  } | {
4813
4813
  type: "none";
4814
4814
  };
4815
- type: "example";
4815
+ type: "extra";
4816
4816
  title: string;
4817
4817
  name: string;
4818
4818
  fullPath: string;
@@ -4903,7 +4903,7 @@ export declare function getWorkshopInstructions({ request, }?: {
4903
4903
  readonly file: string;
4904
4904
  readonly relativePath: "exercises";
4905
4905
  }>;
4906
- export declare function getExamplesInstructions({ request, }?: {
4906
+ export declare function getExtrasInstructions({ request, }?: {
4907
4907
  request?: Request;
4908
4908
  }): Promise<{
4909
4909
  readonly compiled: {
@@ -4916,7 +4916,7 @@ export declare function getExamplesInstructions({ request, }?: {
4916
4916
  readonly error: string;
4917
4917
  };
4918
4918
  readonly file: string;
4919
- readonly relativePath: "examples/README.mdx";
4919
+ readonly relativePath: "extra/README.mdx" | "example/README.mdx" | "examples/README.mdx";
4920
4920
  }>;
4921
4921
  export declare function getWorkshopFinished({ request, }?: {
4922
4922
  request?: Request;
@@ -12,7 +12,7 @@ import { execa } from 'execa';
12
12
  import fsExtra from 'fs-extra';
13
13
  import { globby, isGitIgnored } from 'globby';
14
14
  import { z } from 'zod';
15
- import { cachified, exampleAppCache, playgroundAppCache, problemAppCache, solutionAppCache, directoryEmptyCache, } from "./cache.server.js";
15
+ import { cachified, extraAppCache, playgroundAppCache, problemAppCache, solutionAppCache, directoryEmptyCache, } from "./cache.server.js";
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";
@@ -24,6 +24,57 @@ import { getServerTimeHeader, time } from "./timing.server.js";
24
24
  import { dayjs } from "./utils.server.js";
25
25
  import { getErrorMessage } from "./utils.js";
26
26
  const log = logger('epic:apps');
27
+ const EXTRA_DIRNAME = 'extra';
28
+ const LEGACY_EXAMPLE_DIRNAME = 'example';
29
+ const LEGACY_EXAMPLES_DIRNAME = 'examples';
30
+ const EXTRA_DIR_CANDIDATES = [
31
+ EXTRA_DIRNAME,
32
+ LEGACY_EXAMPLE_DIRNAME,
33
+ LEGACY_EXAMPLES_DIRNAME,
34
+ ];
35
+ async function resolveExtraDir() {
36
+ for (const dirName of EXTRA_DIR_CANDIDATES) {
37
+ const fullPath = path.join(getWorkshopRoot(), dirName);
38
+ if (await exists(fullPath)) {
39
+ return { dirName, fullPath };
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+ async function getExtraDirName() {
45
+ const resolved = await resolveExtraDir();
46
+ return resolved?.dirName ?? EXTRA_DIRNAME;
47
+ }
48
+ function getExtraDirInfoFromPath(fullPath) {
49
+ const normalizedFullPath = fullPath.replace(/\\/g, '/');
50
+ const normalizedRoot = getWorkshopRoot().replace(/\\/g, '/');
51
+ for (const dirName of EXTRA_DIR_CANDIDATES) {
52
+ const prefix = `${normalizedRoot}/${dirName}/`;
53
+ if (normalizedFullPath.startsWith(prefix)) {
54
+ return {
55
+ dirName,
56
+ restOfPath: normalizedFullPath.slice(prefix.length),
57
+ };
58
+ }
59
+ }
60
+ return null;
61
+ }
62
+ function parseExtraAppName(appName) {
63
+ const prefixes = ['extra.', '.extra', 'example.', '.example'];
64
+ for (const prefix of prefixes) {
65
+ if (appName.startsWith(prefix)) {
66
+ let relativePath = appName.slice(prefix.length);
67
+ // Only strip the leading dot if the prefix didn't end with a dot
68
+ // (i.e., for .extra and .example, not for extra. and example.)
69
+ // This preserves hidden directories like .my-hidden-app
70
+ if (!prefix.endsWith('.') && relativePath.startsWith('.')) {
71
+ relativePath = relativePath.slice(1);
72
+ }
73
+ return relativePath.length ? relativePath : null;
74
+ }
75
+ }
76
+ return null;
77
+ }
27
78
  global.__epicshop_apps_initialized__ ??= false;
28
79
  export function setWorkshopRoot(root = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd()) {
29
80
  process.env.EPICSHOP_CONTEXT_CWD = root;
@@ -83,8 +134,8 @@ const SolutionAppSchema = BaseExerciseStepAppSchema.extend({
83
134
  type: z.literal('solution'),
84
135
  problemName: z.string().nullable(),
85
136
  });
86
- const ExampleAppSchema = BaseAppSchema.extend({
87
- type: z.literal('example'),
137
+ const ExtraAppSchema = BaseAppSchema.extend({
138
+ type: z.literal('extra'),
88
139
  });
89
140
  const PlaygroundAppSchema = BaseAppSchema.extend({
90
141
  type: z.literal('playground'),
@@ -131,7 +182,7 @@ const ExerciseStepAppSchema = z.union([ProblemAppSchema, SolutionAppSchema]);
131
182
  const AppSchema = z.union([
132
183
  ExerciseStepAppSchema,
133
184
  PlaygroundAppSchema,
134
- ExampleAppSchema,
185
+ ExtraAppSchema,
135
186
  ]);
136
187
  export function isApp(app) {
137
188
  return AppSchema.safeParse(app).success;
@@ -151,8 +202,8 @@ export function isFirstStepSolutionApp(app) {
151
202
  export function isPlaygroundApp(app) {
152
203
  return isApp(app) && app.type === 'playground';
153
204
  }
154
- export function isExampleApp(app) {
155
- return isApp(app) && app.type === 'example';
205
+ export function isExtraApp(app) {
206
+ return isApp(app) && app.type === 'extra';
156
207
  }
157
208
  export function isExerciseStepApp(app) {
158
209
  return isProblemApp(app) || isSolutionApp(app);
@@ -196,9 +247,10 @@ export async function init(workshopRoot) {
196
247
  if (!getEnv().EPICSHOP_DEPLOYED &&
197
248
  process.env.EPICSHOP_ENABLE_WATCHER === 'true') {
198
249
  const isIgnored = await isGitIgnored({ cwd: getWorkshopRoot() });
250
+ const extraDirName = await getExtraDirName();
199
251
  // watch the README, FINISHED, and package.json for changes that affect the apps
200
252
  const filesToWatch = ['README.mdx', 'FINISHED.mdx', 'package.json'];
201
- const chok = chokidar.watch(['examples', 'playground', 'exercises'], {
253
+ const chok = chokidar.watch([extraDirName, 'playground', 'exercises'], {
202
254
  cwd: getWorkshopRoot(),
203
255
  // we want to load up the modified times immediately
204
256
  ignoreInitial: false,
@@ -211,8 +263,11 @@ export async function init(workshopRoot) {
211
263
  if (filePath.endsWith('playground'))
212
264
  return false;
213
265
  const pathParts = filePath.split(path.sep);
214
- if (pathParts.at(-2) === 'examples')
266
+ const parentDir = pathParts.at(-2);
267
+ if (parentDir &&
268
+ EXTRA_DIR_CANDIDATES.includes(parentDir)) {
215
269
  return false;
270
+ }
216
271
  // steps
217
272
  if (pathParts.at(-3) === 'exercises')
218
273
  return false;
@@ -367,7 +422,7 @@ export const getExercises = requestStorageify(_getExercises);
367
422
  async function _getApps({ timings, request, } = {}) {
368
423
  await init();
369
424
  const apps = await time(async () => {
370
- const [playgroundApp, problemApps, solutionApps, exampleApps] = await Promise.all([
425
+ const [playgroundApp, problemApps, solutionApps, extraApps] = await Promise.all([
371
426
  time(() => getPlaygroundApp({ request, timings }), {
372
427
  type: 'getPlaygroundApp',
373
428
  timings,
@@ -380,8 +435,8 @@ async function _getApps({ timings, request, } = {}) {
380
435
  type: 'getSolutionApps',
381
436
  timings,
382
437
  }),
383
- time(() => getExampleApps({ request, timings }), {
384
- type: 'getExampleApps',
438
+ time(() => getExtraApps({ request, timings }), {
439
+ type: 'getExtraApps',
385
440
  timings,
386
441
  }),
387
442
  ]);
@@ -389,7 +444,7 @@ async function _getApps({ timings, request, } = {}) {
389
444
  playgroundApp,
390
445
  ...problemApps,
391
446
  ...solutionApps,
392
- ...exampleApps,
447
+ ...extraApps,
393
448
  ]
394
449
  .filter(Boolean)
395
450
  .sort((a, b) => {
@@ -401,13 +456,13 @@ async function _getApps({ timings, request, } = {}) {
401
456
  }
402
457
  if (isPlaygroundApp(b))
403
458
  return 1;
404
- if (isExampleApp(a)) {
405
- if (isExampleApp(b))
459
+ if (isExtraApp(a)) {
460
+ if (isExtraApp(b))
406
461
  return a.name.localeCompare(b.name);
407
462
  else
408
463
  return 1;
409
464
  }
410
- if (isExampleApp(b))
465
+ if (isExtraApp(b))
411
466
  return -1;
412
467
  if (a.type === b.type) {
413
468
  if (a.exerciseNumber === b.exerciseNumber) {
@@ -559,9 +614,9 @@ function getPathname(fullPath) {
559
614
  function getAppName(fullPath) {
560
615
  if (/playground\/?$/.test(fullPath))
561
616
  return 'playground';
562
- if (/examples\/.+\/?$/.test(fullPath)) {
563
- const restOfPath = fullPath.replace(`${getWorkshopRoot()}${path.sep}examples${path.sep}`, '');
564
- return `example.${restOfPath.split(path.sep).join('__sep__')}`;
617
+ const extraDirInfo = getExtraDirInfoFromPath(fullPath);
618
+ if (extraDirInfo) {
619
+ return `extra.${extraDirInfo.restOfPath.split('/').join('__sep__')}`;
565
620
  }
566
621
  const appIdInfo = extractNumbersAndTypeFromAppNameOrPath(fullPath);
567
622
  if (appIdInfo) {
@@ -576,12 +631,11 @@ function getAppName(fullPath) {
576
631
  export async function getFullPathFromAppName(appName) {
577
632
  if (appName === 'playground')
578
633
  return path.join(getWorkshopRoot(), 'playground');
579
- if (appName.startsWith('.example')) {
580
- const relativePath = appName
581
- .replace('.example', '')
582
- .split('__sep__')
583
- .join(path.sep);
584
- return path.join(getWorkshopRoot(), 'examples', relativePath);
634
+ const extraRelativePath = parseExtraAppName(appName);
635
+ if (extraRelativePath) {
636
+ const relativePath = extraRelativePath.split('__sep__').join(path.sep);
637
+ const extraDirName = await getExtraDirName();
638
+ return path.join(getWorkshopRoot(), extraDirName, relativePath);
585
639
  }
586
640
  if (appName.includes('__sep__')) {
587
641
  const relativePath = appName.replaceAll('__sep__', path.sep);
@@ -759,12 +813,12 @@ export async function getPlaygroundApp({ timings, request, } = {}) {
759
813
  return null;
760
814
  });
761
815
  }
762
- async function getExampleAppFromPath(fullPath, index, request) {
816
+ async function getExtraAppFromPath(fullPath, index, request) {
763
817
  const dirName = path.basename(fullPath);
764
818
  const compiledReadme = await compileMdxIfExists(path.join(fullPath, 'README.mdx'), { request });
765
819
  const name = getAppName(fullPath);
766
820
  const portNumber = 8000 + index;
767
- const type = 'example';
821
+ const type = 'extra';
768
822
  const title = compiledReadme?.title ?? name;
769
823
  return {
770
824
  name,
@@ -784,44 +838,46 @@ async function getExampleAppFromPath(fullPath, index, request) {
784
838
  }),
785
839
  };
786
840
  }
787
- async function getExampleApps({ timings, request, } = {}) {
788
- const examplesDir = path.join(getWorkshopRoot(), 'examples');
841
+ async function getExtraApps({ timings, request, } = {}) {
842
+ const extraDirInfo = await resolveExtraDir();
843
+ if (!extraDirInfo)
844
+ return [];
789
845
  // Filter to only include directories, not files like README.mdx
790
846
  const entries = await fs.promises
791
- .readdir(examplesDir, { withFileTypes: true })
847
+ .readdir(extraDirInfo.fullPath, { withFileTypes: true })
792
848
  .catch(() => []);
793
- const exampleDirs = entries
849
+ const extraDirs = entries
794
850
  .filter((entry) => {
795
851
  if (entry.isDirectory())
796
852
  return true;
797
- log(`Skipping non-directory in examples: ${entry.name}`);
853
+ log(`Skipping non-directory in extras: ${entry.name}`);
798
854
  return false;
799
855
  })
800
- .map((entry) => path.join(examplesDir, entry.name));
801
- const exampleApps = [];
802
- for (const exampleDir of exampleDirs) {
803
- const index = exampleDirs.indexOf(exampleDir);
804
- const key = `${exampleDir}-${index}`;
805
- const exampleApp = await cachified({
856
+ .map((entry) => path.join(extraDirInfo.fullPath, entry.name));
857
+ const extraApps = [];
858
+ for (const extraDir of extraDirs) {
859
+ const index = extraDirs.indexOf(extraDir);
860
+ const key = `${extraDir}-${index}`;
861
+ const extraApp = await cachified({
806
862
  key,
807
- cache: exampleAppCache,
863
+ cache: extraAppCache,
808
864
  ttl: 1000 * 60 * 5,
809
865
  swr: 1000 * 60 * 60 * 24 * 30,
810
866
  timings,
811
- timingKey: exampleDir.replace(`${examplesDir}${path.sep}`, ''),
867
+ timingKey: extraDir.replace(`${extraDirInfo.fullPath}${path.sep}`, ''),
812
868
  request,
813
- forceFresh: await getForceFreshForDir(exampleAppCache.get(key), exampleDir),
869
+ forceFresh: await getForceFreshForDir(extraAppCache.get(key), extraDir),
814
870
  getFreshValue: async () => {
815
- return getExampleAppFromPath(exampleDir, index, request).catch((error) => {
871
+ return getExtraAppFromPath(extraDir, index, request).catch((error) => {
816
872
  console.error(error);
817
873
  return null;
818
874
  });
819
875
  },
820
876
  });
821
- if (exampleApp)
822
- exampleApps.push(exampleApp);
877
+ if (extraApp)
878
+ extraApps.push(extraApp);
823
879
  }
824
- return exampleApps;
880
+ return extraApps;
825
881
  }
826
882
  async function getSolutionAppFromPath(fullPath, request) {
827
883
  const dirName = path.basename(fullPath);
@@ -996,7 +1052,7 @@ export async function getExerciseApp(params, { request, timings } = {}) {
996
1052
  const { type, exerciseNumber, stepNumber } = result.data;
997
1053
  const apps = (await getApps({ request, timings })).filter(isExerciseStepApp);
998
1054
  const exerciseApp = apps.find((app) => {
999
- if (isExampleApp(app))
1055
+ if (isExtraApp(app))
1000
1056
  return false;
1001
1057
  return (app.exerciseNumber === exerciseNumber &&
1002
1058
  app.stepNumber === stepNumber &&
@@ -1009,7 +1065,24 @@ export async function getExerciseApp(params, { request, timings } = {}) {
1009
1065
  }
1010
1066
  export async function getAppByName(name, { request, timings } = {}) {
1011
1067
  const apps = await getApps({ request, timings });
1012
- return apps.find((a) => a.name === name);
1068
+ // First try exact match
1069
+ const exactMatch = apps.find((a) => a.name === name);
1070
+ if (exactMatch)
1071
+ return exactMatch;
1072
+ // For backward compatibility, check if this is a legacy app name
1073
+ // (e.g., example.my-app) and find the corresponding extra app (extra.my-app)
1074
+ const relativePath = parseExtraAppName(name);
1075
+ if (relativePath) {
1076
+ // Try to find an app with the same relative path but different prefix
1077
+ const alternativePrefixes = ['extra.', 'example.'];
1078
+ for (const prefix of alternativePrefixes) {
1079
+ const alternativeName = `${prefix}${relativePath.split('/').join('__sep__')}`;
1080
+ const match = apps.find((a) => a.name === alternativeName);
1081
+ if (match)
1082
+ return match;
1083
+ }
1084
+ }
1085
+ return undefined;
1013
1086
  }
1014
1087
  export async function getNextExerciseApp(app, { request, timings } = {}) {
1015
1088
  const apps = (await getApps({ request, timings })).filter(isExerciseStepApp);
@@ -1271,8 +1344,8 @@ export function getAppDisplayName(a, allApps) {
1271
1344
  displayName = `🛝 ${a.appName}`;
1272
1345
  }
1273
1346
  }
1274
- else if (isExampleApp(a)) {
1275
- displayName = `📚 ${a.title} (example)`;
1347
+ else if (isExtraApp(a)) {
1348
+ displayName = `📚 ${a.title} (extra)`;
1276
1349
  }
1277
1350
  return displayName;
1278
1351
  }
@@ -1284,16 +1357,18 @@ export async function getWorkshopInstructions({ request, } = {}) {
1284
1357
  });
1285
1358
  return { compiled, file: readmeFilepath, relativePath: 'exercises' };
1286
1359
  }
1287
- export async function getExamplesInstructions({ request, } = {}) {
1288
- const readmeFilepath = path.join(getWorkshopRoot(), 'examples', 'README.mdx');
1360
+ export async function getExtrasInstructions({ request, } = {}) {
1361
+ const extraDirInfo = await resolveExtraDir();
1362
+ const dirName = extraDirInfo?.dirName ?? EXTRA_DIRNAME;
1363
+ const readmeFilepath = path.join(getWorkshopRoot(), dirName, 'README.mdx');
1289
1364
  const compiled = await compileMdx(readmeFilepath, { request }).then((r) => ({ ...r, status: 'success' }), (e) => {
1290
- console.error(`There was an error compiling the examples README.mdx`, readmeFilepath, e);
1365
+ console.error(`There was an error compiling the extras README.mdx`, readmeFilepath, e);
1291
1366
  return { status: 'error', error: getErrorMessage(e) };
1292
1367
  });
1293
1368
  return {
1294
1369
  compiled,
1295
1370
  file: readmeFilepath,
1296
- relativePath: 'examples/README.mdx',
1371
+ relativePath: `${dirName}/README.mdx`,
1297
1372
  };
1298
1373
  }
1299
1374
  export async function getWorkshopFinished({ request, } = {}) {
@@ -1324,16 +1399,27 @@ export function getAppPathFromFilePath(filePath) {
1324
1399
  if (!withinWorkshopRootHalf) {
1325
1400
  return null;
1326
1401
  }
1327
- const [part1, part2, part3] = withinWorkshopRootHalf
1328
- .split(path.sep)
1329
- .filter(Boolean);
1402
+ const pathParts = withinWorkshopRootHalf.split(path.sep).filter(Boolean);
1403
+ const part1 = pathParts[0] ?? '';
1404
+ const part2 = pathParts[1] ?? '';
1405
+ const part3 = pathParts[2] ?? '';
1330
1406
  // Check if the file is in the playground
1331
1407
  if (part1 === 'playground') {
1332
1408
  return path.join(getWorkshopRoot(), 'playground');
1333
1409
  }
1334
- // Check if the file is in an example
1335
- if (part1 === 'examples' && part2) {
1336
- return path.join(getWorkshopRoot(), 'examples', part2);
1410
+ // Check if the file is in an extra app (or legacy examples)
1411
+ if (part1 === EXTRA_DIRNAME ||
1412
+ part1 === LEGACY_EXAMPLE_DIRNAME ||
1413
+ part1 === LEGACY_EXAMPLES_DIRNAME) {
1414
+ if (!part2)
1415
+ return null;
1416
+ invariant(part2.length > 0, 'Expected extra app directory name');
1417
+ const extraRoot = part1 === EXTRA_DIRNAME
1418
+ ? EXTRA_DIRNAME
1419
+ : part1 === LEGACY_EXAMPLE_DIRNAME
1420
+ ? LEGACY_EXAMPLE_DIRNAME
1421
+ : LEGACY_EXAMPLES_DIRNAME;
1422
+ return path.join(getWorkshopRoot(), extraRoot, part2);
1337
1423
  }
1338
1424
  // Check if the file is in an exercise
1339
1425
  if (part1 === 'exercises' && part2 && part3) {
@@ -85,7 +85,7 @@ export declare const problemAppCache: C.Cache<{
85
85
  instructionsCode?: string | undefined;
86
86
  epicVideoEmbeds?: string[] | undefined;
87
87
  }>;
88
- export declare const exampleAppCache: C.Cache<{
88
+ export declare const extraAppCache: C.Cache<{
89
89
  test: {
90
90
  type: "browser";
91
91
  pathname: string;
@@ -96,7 +96,7 @@ export declare const exampleAppCache: C.Cache<{
96
96
  } | {
97
97
  type: "none";
98
98
  };
99
- type: "example";
99
+ type: "extra";
100
100
  title: string;
101
101
  name: string;
102
102
  fullPath: string;
@@ -164,7 +164,7 @@ export function epicCacheReporter({ formatDuration = defaultFormatDuration, perf
164
164
  }
165
165
  export const solutionAppCache = makeSingletonFsCache('SolutionAppCache');
166
166
  export const problemAppCache = makeSingletonFsCache('ProblemAppCache');
167
- export const exampleAppCache = makeSingletonFsCache('ExampleAppCache');
167
+ export const extraAppCache = makeSingletonFsCache('ExtraAppCache');
168
168
  export const playgroundAppCache = makeSingletonFsCache('PlaygroundAppCache');
169
169
  export const diffCodeCache = makeSingletonFsCache('DiffCodeCache');
170
170
  export const diffFilesCache = makeSingletonFsCache('DiffFilesCache');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epic-web/workshop-utils",
3
- "version": "6.61.4",
3
+ "version": "6.62.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },