@open-mercato/shared 0.4.11-develop.1915.8fe74dbc2d → 0.4.11-develop.1917.b6098e78cb

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.
@@ -1,12 +1,27 @@
1
1
  import path from "node:path";
2
2
  import fs from "node:fs";
3
+ const nextConfigNames = [
4
+ "next.config.ts",
5
+ "next.config.js",
6
+ "next.config.mjs"
7
+ ];
8
+ function hasNextConfigFile(appDir) {
9
+ return nextConfigNames.some((configName) => {
10
+ const configPath = path.join(appDir, configName);
11
+ if (!fs.existsSync(configPath)) {
12
+ return false;
13
+ }
14
+ try {
15
+ return fs.statSync(configPath).isFile();
16
+ } catch {
17
+ return false;
18
+ }
19
+ });
20
+ }
3
21
  function findAppRoot(startDir = process.cwd()) {
4
22
  let current = startDir;
5
- while (current !== path.dirname(current)) {
6
- const configTs = path.join(current, "next.config.ts");
7
- const configJs = path.join(current, "next.config.js");
8
- const configMjs = path.join(current, "next.config.mjs");
9
- if (fs.existsSync(configTs) || fs.existsSync(configJs) || fs.existsSync(configMjs)) {
23
+ while (true) {
24
+ if (hasNextConfigFile(current)) {
10
25
  const mercatoDir = path.join(current, ".mercato");
11
26
  const generatedDir = path.join(mercatoDir, "generated");
12
27
  if (fs.existsSync(generatedDir)) {
@@ -14,7 +29,11 @@ function findAppRoot(startDir = process.cwd()) {
14
29
  }
15
30
  return { appDir: current, mercatoDir, generatedDir };
16
31
  }
17
- current = path.dirname(current);
32
+ const parent = path.dirname(current);
33
+ if (parent === current) {
34
+ break;
35
+ }
36
+ current = parent;
18
37
  }
19
38
  return null;
20
39
  }
@@ -26,8 +45,7 @@ function findAllApps(rootDir) {
26
45
  for (const entry of entries) {
27
46
  if (!entry.isDirectory()) continue;
28
47
  const appDir = path.join(appsDir, entry.name);
29
- const hasNextConfig = fs.existsSync(path.join(appDir, "next.config.ts")) || fs.existsSync(path.join(appDir, "next.config.js")) || fs.existsSync(path.join(appDir, "next.config.mjs"));
30
- if (!hasNextConfig) continue;
48
+ if (!hasNextConfigFile(appDir)) continue;
31
49
  const mercatoDir = path.join(appDir, ".mercato");
32
50
  const generatedDir = path.join(mercatoDir, "generated");
33
51
  if (fs.existsSync(generatedDir)) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/bootstrap/appResolver.ts"],
4
- "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\n\nexport interface AppRoot {\n appDir: string\n mercatoDir: string\n generatedDir: string\n}\n\n/**\n * Find the Next.js app root by searching for next.config.ts/js/mjs.\n *\n * Starts from the given directory (defaults to cwd) and walks up the\n * directory tree until it finds a Next.js config file with a .mercato/generated\n * directory.\n *\n * @param startDir - Directory to start searching from (defaults to process.cwd())\n * @returns The resolved app root paths, or null if not found\n */\nexport function findAppRoot(startDir: string = process.cwd()): AppRoot | null {\n let current = startDir\n\n while (current !== path.dirname(current)) {\n const configTs = path.join(current, 'next.config.ts')\n const configJs = path.join(current, 'next.config.js')\n const configMjs = path.join(current, 'next.config.mjs')\n\n if (fs.existsSync(configTs) || fs.existsSync(configJs) || fs.existsSync(configMjs)) {\n const mercatoDir = path.join(current, '.mercato')\n const generatedDir = path.join(mercatoDir, 'generated')\n\n // Only return if .mercato/generated exists\n if (fs.existsSync(generatedDir)) {\n return { appDir: current, mercatoDir, generatedDir }\n }\n\n // Found Next.js config but no .mercato/generated - return anyway for generate command\n // The caller can decide whether to create the directory\n return { appDir: current, mercatoDir, generatedDir }\n }\n\n current = path.dirname(current)\n }\n\n return null\n}\n\n/**\n * Find all apps with .mercato directories in a monorepo.\n *\n * Scans the apps/ directory for Next.js apps with .mercato/generated directories.\n *\n * @param rootDir - The monorepo root directory\n * @returns Array of app root paths\n */\nexport function findAllApps(rootDir: string): AppRoot[] {\n const appsDir = path.join(rootDir, 'apps')\n if (!fs.existsSync(appsDir)) return []\n\n const apps: AppRoot[] = []\n const entries = fs.readdirSync(appsDir, { withFileTypes: true })\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n\n const appDir = path.join(appsDir, entry.name)\n\n // Check for Next.js config\n const hasNextConfig =\n fs.existsSync(path.join(appDir, 'next.config.ts')) ||\n fs.existsSync(path.join(appDir, 'next.config.js')) ||\n fs.existsSync(path.join(appDir, 'next.config.mjs'))\n\n if (!hasNextConfig) continue\n\n const mercatoDir = path.join(appDir, '.mercato')\n const generatedDir = path.join(mercatoDir, 'generated')\n\n if (fs.existsSync(generatedDir)) {\n apps.push({ appDir, mercatoDir, generatedDir })\n }\n }\n\n return apps\n}\n"],
5
- "mappings": "AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAkBR,SAAS,YAAY,WAAmB,QAAQ,IAAI,GAAmB;AAC5E,MAAI,UAAU;AAEd,SAAO,YAAY,KAAK,QAAQ,OAAO,GAAG;AACxC,UAAM,WAAW,KAAK,KAAK,SAAS,gBAAgB;AACpD,UAAM,WAAW,KAAK,KAAK,SAAS,gBAAgB;AACpD,UAAM,YAAY,KAAK,KAAK,SAAS,iBAAiB;AAEtD,QAAI,GAAG,WAAW,QAAQ,KAAK,GAAG,WAAW,QAAQ,KAAK,GAAG,WAAW,SAAS,GAAG;AAClF,YAAM,aAAa,KAAK,KAAK,SAAS,UAAU;AAChD,YAAM,eAAe,KAAK,KAAK,YAAY,WAAW;AAGtD,UAAI,GAAG,WAAW,YAAY,GAAG;AAC/B,eAAO,EAAE,QAAQ,SAAS,YAAY,aAAa;AAAA,MACrD;AAIA,aAAO,EAAE,QAAQ,SAAS,YAAY,aAAa;AAAA,IACrD;AAEA,cAAU,KAAK,QAAQ,OAAO;AAAA,EAChC;AAEA,SAAO;AACT;AAUO,SAAS,YAAY,SAA4B;AACtD,QAAM,UAAU,KAAK,KAAK,SAAS,MAAM;AACzC,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG,QAAO,CAAC;AAErC,QAAM,OAAkB,CAAC;AACzB,QAAM,UAAU,GAAG,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAE/D,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,YAAY,EAAG;AAE1B,UAAM,SAAS,KAAK,KAAK,SAAS,MAAM,IAAI;AAG5C,UAAM,gBACJ,GAAG,WAAW,KAAK,KAAK,QAAQ,gBAAgB,CAAC,KACjD,GAAG,WAAW,KAAK,KAAK,QAAQ,gBAAgB,CAAC,KACjD,GAAG,WAAW,KAAK,KAAK,QAAQ,iBAAiB,CAAC;AAEpD,QAAI,CAAC,cAAe;AAEpB,UAAM,aAAa,KAAK,KAAK,QAAQ,UAAU;AAC/C,UAAM,eAAe,KAAK,KAAK,YAAY,WAAW;AAEtD,QAAI,GAAG,WAAW,YAAY,GAAG;AAC/B,WAAK,KAAK,EAAE,QAAQ,YAAY,aAAa,CAAC;AAAA,IAChD;AAAA,EACF;AAEA,SAAO;AACT;",
4
+ "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\n\nexport interface AppRoot {\n appDir: string\n mercatoDir: string\n generatedDir: string\n}\n\nconst nextConfigNames = [\n 'next.config.ts',\n 'next.config.js',\n 'next.config.mjs',\n] as const\n\nfunction hasNextConfigFile(appDir: string): boolean {\n return nextConfigNames.some((configName) => {\n const configPath = path.join(appDir, configName)\n\n if (!fs.existsSync(configPath)) {\n return false\n }\n\n try {\n return fs.statSync(configPath).isFile()\n } catch {\n return false\n }\n })\n}\n\n/**\n * Find the Next.js app root by searching for next.config.ts/js/mjs.\n *\n * Starts from the given directory (defaults to cwd) and walks up the\n * directory tree until it finds a Next.js config file with a .mercato/generated\n * directory.\n *\n * @param startDir - Directory to start searching from (defaults to process.cwd())\n * @returns The resolved app root paths, or null if not found\n */\nexport function findAppRoot(startDir: string = process.cwd()): AppRoot | null {\n let current = startDir\n\n while (true) {\n if (hasNextConfigFile(current)) {\n const mercatoDir = path.join(current, '.mercato')\n const generatedDir = path.join(mercatoDir, 'generated')\n\n // Only return if .mercato/generated exists\n if (fs.existsSync(generatedDir)) {\n return { appDir: current, mercatoDir, generatedDir }\n }\n\n // Found Next.js config but no .mercato/generated - return anyway for generate command\n // The caller can decide whether to create the directory\n return { appDir: current, mercatoDir, generatedDir }\n }\n\n const parent = path.dirname(current)\n if (parent === current) {\n break\n }\n\n current = parent\n }\n\n return null\n}\n\n/**\n * Find all apps with .mercato directories in a monorepo.\n *\n * Scans the apps/ directory for Next.js apps with .mercato/generated directories.\n *\n * @param rootDir - The monorepo root directory\n * @returns Array of app root paths\n */\nexport function findAllApps(rootDir: string): AppRoot[] {\n const appsDir = path.join(rootDir, 'apps')\n if (!fs.existsSync(appsDir)) return []\n\n const apps: AppRoot[] = []\n const entries = fs.readdirSync(appsDir, { withFileTypes: true })\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n\n const appDir = path.join(appsDir, entry.name)\n\n if (!hasNextConfigFile(appDir)) continue\n\n const mercatoDir = path.join(appDir, '.mercato')\n const generatedDir = path.join(mercatoDir, 'generated')\n\n if (fs.existsSync(generatedDir)) {\n apps.push({ appDir, mercatoDir, generatedDir })\n }\n }\n\n return apps\n}\n"],
5
+ "mappings": "AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAQf,MAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,kBAAkB,QAAyB;AAClD,SAAO,gBAAgB,KAAK,CAAC,eAAe;AAC1C,UAAM,aAAa,KAAK,KAAK,QAAQ,UAAU;AAE/C,QAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI;AACF,aAAO,GAAG,SAAS,UAAU,EAAE,OAAO;AAAA,IACxC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAYO,SAAS,YAAY,WAAmB,QAAQ,IAAI,GAAmB;AAC5E,MAAI,UAAU;AAEd,SAAO,MAAM;AACX,QAAI,kBAAkB,OAAO,GAAG;AAC9B,YAAM,aAAa,KAAK,KAAK,SAAS,UAAU;AAChD,YAAM,eAAe,KAAK,KAAK,YAAY,WAAW;AAGtD,UAAI,GAAG,WAAW,YAAY,GAAG;AAC/B,eAAO,EAAE,QAAQ,SAAS,YAAY,aAAa;AAAA,MACrD;AAIA,aAAO,EAAE,QAAQ,SAAS,YAAY,aAAa;AAAA,IACrD;AAEA,UAAM,SAAS,KAAK,QAAQ,OAAO;AACnC,QAAI,WAAW,SAAS;AACtB;AAAA,IACF;AAEA,cAAU;AAAA,EACZ;AAEA,SAAO;AACT;AAUO,SAAS,YAAY,SAA4B;AACtD,QAAM,UAAU,KAAK,KAAK,SAAS,MAAM;AACzC,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG,QAAO,CAAC;AAErC,QAAM,OAAkB,CAAC;AACzB,QAAM,UAAU,GAAG,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAE/D,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,YAAY,EAAG;AAE1B,UAAM,SAAS,KAAK,KAAK,SAAS,MAAM,IAAI;AAE5C,QAAI,CAAC,kBAAkB,MAAM,EAAG;AAEhC,UAAM,aAAa,KAAK,KAAK,QAAQ,UAAU;AAC/C,UAAM,eAAe,KAAK,KAAK,YAAY,WAAW;AAEtD,QAAI,GAAG,WAAW,YAAY,GAAG;AAC/B,WAAK,KAAK,EAAE,QAAQ,YAAY,aAAa,CAAC;AAAA,IAChD;AAAA,EACF;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.4.11-develop.1915.8fe74dbc2d";
1
+ const APP_VERSION = "0.4.11-develop.1917.b6098e78cb";
2
2
  const appVersion = APP_VERSION;
3
3
  export {
4
4
  APP_VERSION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/version.ts"],
4
- "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.11-develop.1915.8fe74dbc2d'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.11-develop.1917.b6098e78cb'\nexport const appVersion = APP_VERSION\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/shared",
3
- "version": "0.4.11-develop.1915.8fe74dbc2d",
3
+ "version": "0.4.11-develop.1917.b6098e78cb",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -21,6 +21,17 @@ function createApp(rootDir: string, relativeAppDir: string, configName: NextConf
21
21
  return { appDir, mercatoDir, generatedDir }
22
22
  }
23
23
 
24
+ function createInvalidConfigDirectory(rootDir: string, relativeAppDir: string, configName: NextConfigName): AppRoot {
25
+ const appDir = path.join(rootDir, relativeAppDir)
26
+ const mercatoDir = path.join(appDir, '.mercato')
27
+ const generatedDir = path.join(mercatoDir, 'generated')
28
+
29
+ fs.mkdirSync(path.join(appDir, configName), { recursive: true })
30
+ fs.mkdirSync(generatedDir, { recursive: true })
31
+
32
+ return { appDir, mercatoDir, generatedDir }
33
+ }
34
+
24
35
  describe('appResolver', () => {
25
36
  let tempDir: string
26
37
 
@@ -61,6 +72,16 @@ describe('appResolver', () => {
61
72
  expect(findAppRoot(nestedDir)).toEqual(innerApp)
62
73
  })
63
74
 
75
+ it('ignores directories masquerading as Next.js config files', () => {
76
+ const outerApp = createApp(tempDir, 'apps/outer', 'next.config.ts')
77
+ const invalidInnerApp = createInvalidConfigDirectory(outerApp.appDir, 'examples/inner', 'next.config.js')
78
+ const nestedDir = path.join(invalidInnerApp.appDir, 'src')
79
+
80
+ fs.mkdirSync(nestedDir, { recursive: true })
81
+
82
+ expect(findAppRoot(nestedDir)).toEqual(outerApp)
83
+ })
84
+
64
85
  it('returns null when no Next.js app can be found', () => {
65
86
  const nestedDir = path.join(tempDir, 'packages', 'shared', 'src')
66
87
 
@@ -80,6 +101,9 @@ describe('appResolver', () => {
80
101
  createApp(tempDir, 'apps/docs', 'next.config.js')
81
102
  createApp(tempDir, 'apps/admin', 'next.config.mjs')
82
103
  createApp(tempDir, 'apps/incomplete', 'next.config.ts', false)
104
+ createInvalidConfigDirectory(tempDir, 'apps/not-really-an-app', 'next.config.ts')
105
+
106
+ fs.mkdirSync(path.join(tempDir, 'apps', 'folder-without-next-config'), { recursive: true })
83
107
 
84
108
  fs.writeFileSync(path.join(tempDir, 'apps', 'README.md'), 'not an app')
85
109
 
@@ -7,6 +7,28 @@ export interface AppRoot {
7
7
  generatedDir: string
8
8
  }
9
9
 
10
+ const nextConfigNames = [
11
+ 'next.config.ts',
12
+ 'next.config.js',
13
+ 'next.config.mjs',
14
+ ] as const
15
+
16
+ function hasNextConfigFile(appDir: string): boolean {
17
+ return nextConfigNames.some((configName) => {
18
+ const configPath = path.join(appDir, configName)
19
+
20
+ if (!fs.existsSync(configPath)) {
21
+ return false
22
+ }
23
+
24
+ try {
25
+ return fs.statSync(configPath).isFile()
26
+ } catch {
27
+ return false
28
+ }
29
+ })
30
+ }
31
+
10
32
  /**
11
33
  * Find the Next.js app root by searching for next.config.ts/js/mjs.
12
34
  *
@@ -20,12 +42,8 @@ export interface AppRoot {
20
42
  export function findAppRoot(startDir: string = process.cwd()): AppRoot | null {
21
43
  let current = startDir
22
44
 
23
- while (current !== path.dirname(current)) {
24
- const configTs = path.join(current, 'next.config.ts')
25
- const configJs = path.join(current, 'next.config.js')
26
- const configMjs = path.join(current, 'next.config.mjs')
27
-
28
- if (fs.existsSync(configTs) || fs.existsSync(configJs) || fs.existsSync(configMjs)) {
45
+ while (true) {
46
+ if (hasNextConfigFile(current)) {
29
47
  const mercatoDir = path.join(current, '.mercato')
30
48
  const generatedDir = path.join(mercatoDir, 'generated')
31
49
 
@@ -39,7 +57,12 @@ export function findAppRoot(startDir: string = process.cwd()): AppRoot | null {
39
57
  return { appDir: current, mercatoDir, generatedDir }
40
58
  }
41
59
 
42
- current = path.dirname(current)
60
+ const parent = path.dirname(current)
61
+ if (parent === current) {
62
+ break
63
+ }
64
+
65
+ current = parent
43
66
  }
44
67
 
45
68
  return null
@@ -65,13 +88,7 @@ export function findAllApps(rootDir: string): AppRoot[] {
65
88
 
66
89
  const appDir = path.join(appsDir, entry.name)
67
90
 
68
- // Check for Next.js config
69
- const hasNextConfig =
70
- fs.existsSync(path.join(appDir, 'next.config.ts')) ||
71
- fs.existsSync(path.join(appDir, 'next.config.js')) ||
72
- fs.existsSync(path.join(appDir, 'next.config.mjs'))
73
-
74
- if (!hasNextConfig) continue
91
+ if (!hasNextConfigFile(appDir)) continue
75
92
 
76
93
  const mercatoDir = path.join(appDir, '.mercato')
77
94
  const generatedDir = path.join(mercatoDir, 'generated')