@sanity/cli-core 0.0.0-20260410130107
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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/SanityCommand.js +196 -0
- package/dist/SanityCommand.js.map +1 -0
- package/dist/_exports/index.d.ts +1391 -0
- package/dist/_exports/index.js +39 -0
- package/dist/_exports/index.js.map +1 -0
- package/dist/_exports/package-manager.d.ts +33 -0
- package/dist/_exports/package-manager.js +3 -0
- package/dist/_exports/package-manager.js.map +1 -0
- package/dist/_exports/request.d.ts +79 -0
- package/dist/_exports/request.js +7 -0
- package/dist/_exports/request.js.map +1 -0
- package/dist/_exports/ux.d.ts +75 -0
- package/dist/_exports/ux.js +7 -0
- package/dist/_exports/ux.js.map +1 -0
- package/dist/config/cli/getCliConfig.js +68 -0
- package/dist/config/cli/getCliConfig.js.map +1 -0
- package/dist/config/cli/getCliConfigSync.js +51 -0
- package/dist/config/cli/getCliConfigSync.js.map +1 -0
- package/dist/config/cli/schemas.js +62 -0
- package/dist/config/cli/schemas.js.map +1 -0
- package/dist/config/cli/types/cliConfig.js +5 -0
- package/dist/config/cli/types/cliConfig.js.map +1 -0
- package/dist/config/cli/types/userViteConfig.js +5 -0
- package/dist/config/cli/types/userViteConfig.js.map +1 -0
- package/dist/config/findProjectRoot.js +62 -0
- package/dist/config/findProjectRoot.js.map +1 -0
- package/dist/config/findProjectRootSync.js +88 -0
- package/dist/config/findProjectRootSync.js.map +1 -0
- package/dist/config/studio/getStudioConfig.js +13 -0
- package/dist/config/studio/getStudioConfig.js.map +1 -0
- package/dist/config/studio/getStudioWorkspaces.js +63 -0
- package/dist/config/studio/getStudioWorkspaces.js.map +1 -0
- package/dist/config/studio/isStudioConfig.js +19 -0
- package/dist/config/studio/isStudioConfig.js.map +1 -0
- package/dist/config/studio/readStudioConfig.js +82 -0
- package/dist/config/studio/readStudioConfig.js.map +1 -0
- package/dist/config/studio/readStudioConfig.worker.js +25 -0
- package/dist/config/studio/readStudioConfig.worker.js.map +1 -0
- package/dist/config/util/configPathsSync.js +85 -0
- package/dist/config/util/configPathsSync.js.map +1 -0
- package/dist/config/util/findAppConfigPath.js +22 -0
- package/dist/config/util/findAppConfigPath.js.map +1 -0
- package/dist/config/util/findConfigsPaths.js +21 -0
- package/dist/config/util/findConfigsPaths.js.map +1 -0
- package/dist/config/util/findStudioConfigPath.js +52 -0
- package/dist/config/util/findStudioConfigPath.js.map +1 -0
- package/dist/config/util/isSanityV2StudioRoot.js +22 -0
- package/dist/config/util/isSanityV2StudioRoot.js.map +1 -0
- package/dist/config/util/recursivelyResolveProjectRoot.js +28 -0
- package/dist/config/util/recursivelyResolveProjectRoot.js.map +1 -0
- package/dist/debug.js +15 -0
- package/dist/debug.js.map +1 -0
- package/dist/errors/NonInteractiveError.js +18 -0
- package/dist/errors/NonInteractiveError.js.map +1 -0
- package/dist/errors/NotFoundError.js +27 -0
- package/dist/errors/NotFoundError.js.map +1 -0
- package/dist/errors/ProjectRootNotFoundError.js +35 -0
- package/dist/errors/ProjectRootNotFoundError.js.map +1 -0
- package/dist/exitCodes.js +17 -0
- package/dist/exitCodes.js.map +1 -0
- package/dist/loaders/studio/studioWorkerLoader.worker.js +205 -0
- package/dist/loaders/studio/studioWorkerLoader.worker.js.map +1 -0
- package/dist/loaders/studio/studioWorkerTask.js +90 -0
- package/dist/loaders/studio/studioWorkerTask.js.map +1 -0
- package/dist/loaders/tsx/tsxWorkerLoader.worker.js +11 -0
- package/dist/loaders/tsx/tsxWorkerLoader.worker.js.map +1 -0
- package/dist/loaders/tsx/tsxWorkerTask.js +34 -0
- package/dist/loaders/tsx/tsxWorkerTask.js.map +1 -0
- package/dist/request/createRequester.js +83 -0
- package/dist/request/createRequester.js.map +1 -0
- package/dist/services/apiClient.js +97 -0
- package/dist/services/apiClient.js.map +1 -0
- package/dist/services/cliTokenCache.js +25 -0
- package/dist/services/cliTokenCache.js.map +1 -0
- package/dist/services/cliUserConfig.js +144 -0
- package/dist/services/cliUserConfig.js.map +1 -0
- package/dist/services/getCliToken.js +26 -0
- package/dist/services/getCliToken.js.map +1 -0
- package/dist/telemetry/getCliTelemetry.js +47 -0
- package/dist/telemetry/getCliTelemetry.js.map +1 -0
- package/dist/telemetry/getTelemetryBaseInfo.js +33 -0
- package/dist/telemetry/getTelemetryBaseInfo.js.map +1 -0
- package/dist/telemetry/readNDJSON.js +18 -0
- package/dist/telemetry/readNDJSON.js.map +1 -0
- package/dist/telemetry/types.js +5 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/util/doImport.js +17 -0
- package/dist/util/doImport.js.map +1 -0
- package/dist/util/environment/getStudioEnvironmentVariables.js +36 -0
- package/dist/util/environment/getStudioEnvironmentVariables.js.map +1 -0
- package/dist/util/environment/mockBrowserEnvironment.js +47 -0
- package/dist/util/environment/mockBrowserEnvironment.js.map +1 -0
- package/dist/util/environment/setupBrowserStubs.js +42 -0
- package/dist/util/environment/setupBrowserStubs.js.map +1 -0
- package/dist/util/environment/stubs.js +142 -0
- package/dist/util/environment/stubs.js.map +1 -0
- package/dist/util/fileExists.js +13 -0
- package/dist/util/fileExists.js.map +1 -0
- package/dist/util/generateHelpUrl.js +11 -0
- package/dist/util/generateHelpUrl.js.map +1 -0
- package/dist/util/getEmptyAuth.js +16 -0
- package/dist/util/getEmptyAuth.js.map +1 -0
- package/dist/util/getSanityEnvVar.js +24 -0
- package/dist/util/getSanityEnvVar.js.map +1 -0
- package/dist/util/getSanityUrl.js +9 -0
- package/dist/util/getSanityUrl.js.map +1 -0
- package/dist/util/importModule.js +32 -0
- package/dist/util/importModule.js.map +1 -0
- package/dist/util/isCi.js +7 -0
- package/dist/util/isCi.js.map +1 -0
- package/dist/util/isInteractive.js +5 -0
- package/dist/util/isInteractive.js.map +1 -0
- package/dist/util/isRecord.js +11 -0
- package/dist/util/isRecord.js.map +1 -0
- package/dist/util/isStaging.js +10 -0
- package/dist/util/isStaging.js.map +1 -0
- package/dist/util/isTrueish.js +10 -0
- package/dist/util/isTrueish.js.map +1 -0
- package/dist/util/normalizePath.js +12 -0
- package/dist/util/normalizePath.js.map +1 -0
- package/dist/util/packageManager.js +55 -0
- package/dist/util/packageManager.js.map +1 -0
- package/dist/util/promisifyWorker.js +72 -0
- package/dist/util/promisifyWorker.js.map +1 -0
- package/dist/util/readJsonFile.js +26 -0
- package/dist/util/readJsonFile.js.map +1 -0
- package/dist/util/readJsonFileSync.js +26 -0
- package/dist/util/readJsonFileSync.js.map +1 -0
- package/dist/util/readPackageJson.js +74 -0
- package/dist/util/readPackageJson.js.map +1 -0
- package/dist/util/resolveLocalPackage.js +82 -0
- package/dist/util/resolveLocalPackage.js.map +1 -0
- package/dist/util/safeStructuredClone.js +43 -0
- package/dist/util/safeStructuredClone.js.map +1 -0
- package/dist/util/tryGetDefaultExport.js +18 -0
- package/dist/util/tryGetDefaultExport.js.map +1 -0
- package/dist/util/writeJsonFileSync.js +19 -0
- package/dist/util/writeJsonFileSync.js.map +1 -0
- package/dist/ux/boxen.js +3 -0
- package/dist/ux/boxen.js.map +1 -0
- package/dist/ux/colorizeJson.js +32 -0
- package/dist/ux/colorizeJson.js.map +1 -0
- package/dist/ux/logSymbols.js +3 -0
- package/dist/ux/logSymbols.js.map +1 -0
- package/dist/ux/prompts.js +51 -0
- package/dist/ux/prompts.js.map +1 -0
- package/dist/ux/spinner.js +3 -0
- package/dist/ux/spinner.js.map +1 -0
- package/dist/ux/timer.js +29 -0
- package/dist/ux/timer.js.map +1 -0
- package/package.json +111 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { moduleResolve } from 'import-meta-resolve';
|
|
4
|
+
import { doImport } from '../doImport.js';
|
|
5
|
+
/**
|
|
6
|
+
* Loads the `getStudioEnvironmentVariables` function from the studio's
|
|
7
|
+
* installed `sanity` package and returns the environment variables.
|
|
8
|
+
*
|
|
9
|
+
* This is used to ensure we're using the same version of environment variable
|
|
10
|
+
* logic as the studio itself.
|
|
11
|
+
*
|
|
12
|
+
* @param rootPath - The root path of the Sanity Studio project
|
|
13
|
+
* @returns Object containing studio environment variables
|
|
14
|
+
* @internal
|
|
15
|
+
*/ export async function getStudioEnvironmentVariables(rootPath) {
|
|
16
|
+
// Create a fake config URL - doesn't have to be correct, just need the root path
|
|
17
|
+
const fakeConfigUrl = pathToFileURL(resolve(rootPath, 'sanity.config.mjs'));
|
|
18
|
+
// Load `getStudioEnvironmentVariables` from the `sanity/cli` module installed
|
|
19
|
+
// relative to where the studio is located, instead of resolving from where this CLI is
|
|
20
|
+
// running, in order to ensure we're using the same version as the studio would.
|
|
21
|
+
const sanityCliUrl = moduleResolve('sanity/cli', fakeConfigUrl);
|
|
22
|
+
try {
|
|
23
|
+
const { getStudioEnvironmentVariables: getEnvVars } = await doImport(sanityCliUrl.href);
|
|
24
|
+
if (typeof getEnvVars !== 'function') {
|
|
25
|
+
throw new TypeError('Expected `getStudioEnvironmentVariables` from `sanity/cli` to be a function');
|
|
26
|
+
}
|
|
27
|
+
return getEnvVars(rootPath);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
30
|
+
throw new Error(`Failed to import getStudioEnvironmentVariables from sanity/cli module: ${message}`, {
|
|
31
|
+
cause: err
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=getStudioEnvironmentVariables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/environment/getStudioEnvironmentVariables.ts"],"sourcesContent":["import {resolve} from 'node:path'\nimport {pathToFileURL} from 'node:url'\n\nimport {moduleResolve} from 'import-meta-resolve'\n\nimport {doImport} from '../doImport.js'\n\n/**\n * Loads the `getStudioEnvironmentVariables` function from the studio's\n * installed `sanity` package and returns the environment variables.\n *\n * This is used to ensure we're using the same version of environment variable\n * logic as the studio itself.\n *\n * @param rootPath - The root path of the Sanity Studio project\n * @returns Object containing studio environment variables\n * @internal\n */\nexport async function getStudioEnvironmentVariables(\n rootPath: string,\n): Promise<Record<string, string>> {\n // Create a fake config URL - doesn't have to be correct, just need the root path\n const fakeConfigUrl = pathToFileURL(resolve(rootPath, 'sanity.config.mjs'))\n\n // Load `getStudioEnvironmentVariables` from the `sanity/cli` module installed\n // relative to where the studio is located, instead of resolving from where this CLI is\n // running, in order to ensure we're using the same version as the studio would.\n const sanityCliUrl = moduleResolve('sanity/cli', fakeConfigUrl)\n try {\n const {getStudioEnvironmentVariables: getEnvVars} = await doImport(sanityCliUrl.href)\n if (typeof getEnvVars !== 'function') {\n throw new TypeError(\n 'Expected `getStudioEnvironmentVariables` from `sanity/cli` to be a function',\n )\n }\n return getEnvVars(rootPath)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Failed to import getStudioEnvironmentVariables from sanity/cli module: ${message}`,\n {cause: err},\n )\n }\n}\n"],"names":["resolve","pathToFileURL","moduleResolve","doImport","getStudioEnvironmentVariables","rootPath","fakeConfigUrl","sanityCliUrl","getEnvVars","href","TypeError","err","message","Error","String","cause"],"mappings":"AAAA,SAAQA,OAAO,QAAO,YAAW;AACjC,SAAQC,aAAa,QAAO,WAAU;AAEtC,SAAQC,aAAa,QAAO,sBAAqB;AAEjD,SAAQC,QAAQ,QAAO,iBAAgB;AAEvC;;;;;;;;;;CAUC,GACD,OAAO,eAAeC,8BACpBC,QAAgB;IAEhB,iFAAiF;IACjF,MAAMC,gBAAgBL,cAAcD,QAAQK,UAAU;IAEtD,8EAA8E;IAC9E,uFAAuF;IACvF,gFAAgF;IAChF,MAAME,eAAeL,cAAc,cAAcI;IACjD,IAAI;QACF,MAAM,EAACF,+BAA+BI,UAAU,EAAC,GAAG,MAAML,SAASI,aAAaE,IAAI;QACpF,IAAI,OAAOD,eAAe,YAAY;YACpC,MAAM,IAAIE,UACR;QAEJ;QACA,OAAOF,WAAWH;IACpB,EAAE,OAAOM,KAAK;QACZ,MAAMC,UAAUD,eAAeE,QAAQF,IAAIC,OAAO,GAAGE,OAAOH;QAC5D,MAAM,IAAIE,MACR,CAAC,uEAAuE,EAAED,SAAS,EACnF;YAACG,OAAOJ;QAAG;IAEf;AACF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// TODO(SDK-983): Consider moving to @sanity/cli — only used there
|
|
2
|
+
import { getStudioEnvironmentVariables } from './getStudioEnvironmentVariables.js';
|
|
3
|
+
import { setupBrowserStubs } from './setupBrowserStubs.js';
|
|
4
|
+
/**
|
|
5
|
+
* Mocks a browser-like environment for processes in the main thread by:
|
|
6
|
+
* - Injecting browser globals (window, document, ResizeObserver, etc.)
|
|
7
|
+
* - Loading studio environment variables from the project's sanity installation into process.env
|
|
8
|
+
*
|
|
9
|
+
* This is useful for commands like `sanity exec` that have to run user scripts
|
|
10
|
+
* in the main thread of the process (but in a child process).
|
|
11
|
+
*
|
|
12
|
+
* Be cautious when using this, since it will pollute the global namespace with browser globals.
|
|
13
|
+
*
|
|
14
|
+
* If your code can run in a worker thread, you should use the `studioWorkerTask` function instead.
|
|
15
|
+
*
|
|
16
|
+
* @param basePath - The root path of the Sanity Studio project
|
|
17
|
+
* @returns A cleanup function that removes the injected globals and environment variables
|
|
18
|
+
* @internal
|
|
19
|
+
*/ export async function mockBrowserEnvironment(basePath) {
|
|
20
|
+
// 1. Setup browser globals
|
|
21
|
+
const cleanupBrowserStubs = await setupBrowserStubs();
|
|
22
|
+
// 2. Load and set environment variables into process.env
|
|
23
|
+
const envVars = await getStudioEnvironmentVariables(basePath);
|
|
24
|
+
const setEnvKeys = [];
|
|
25
|
+
const originalEnvValues = {};
|
|
26
|
+
for (const [key, value] of Object.entries(envVars)){
|
|
27
|
+
// Store original value (if any) so we can restore it
|
|
28
|
+
originalEnvValues[key] = process.env[key];
|
|
29
|
+
process.env[key] = value;
|
|
30
|
+
setEnvKeys.push(key);
|
|
31
|
+
}
|
|
32
|
+
// Return cleanup function
|
|
33
|
+
return ()=>{
|
|
34
|
+
// Restore or delete environment variables
|
|
35
|
+
for (const key of setEnvKeys){
|
|
36
|
+
if (originalEnvValues[key] === undefined) {
|
|
37
|
+
delete process.env[key];
|
|
38
|
+
} else {
|
|
39
|
+
process.env[key] = originalEnvValues[key];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Clean up browser stubs
|
|
43
|
+
cleanupBrowserStubs();
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=mockBrowserEnvironment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/environment/mockBrowserEnvironment.ts"],"sourcesContent":["// TODO(SDK-983): Consider moving to @sanity/cli — only used there\nimport {getStudioEnvironmentVariables} from './getStudioEnvironmentVariables.js'\nimport {setupBrowserStubs} from './setupBrowserStubs.js'\n\n/**\n * Mocks a browser-like environment for processes in the main thread by:\n * - Injecting browser globals (window, document, ResizeObserver, etc.)\n * - Loading studio environment variables from the project's sanity installation into process.env\n *\n * This is useful for commands like `sanity exec` that have to run user scripts\n * in the main thread of the process (but in a child process).\n *\n * Be cautious when using this, since it will pollute the global namespace with browser globals.\n *\n * If your code can run in a worker thread, you should use the `studioWorkerTask` function instead.\n *\n * @param basePath - The root path of the Sanity Studio project\n * @returns A cleanup function that removes the injected globals and environment variables\n * @internal\n */\nexport async function mockBrowserEnvironment(basePath: string): Promise<() => void> {\n // 1. Setup browser globals\n const cleanupBrowserStubs = await setupBrowserStubs()\n\n // 2. Load and set environment variables into process.env\n const envVars = await getStudioEnvironmentVariables(basePath)\n const setEnvKeys: string[] = []\n const originalEnvValues: Record<string, string | undefined> = {}\n\n for (const [key, value] of Object.entries(envVars)) {\n // Store original value (if any) so we can restore it\n originalEnvValues[key] = process.env[key]\n process.env[key] = value\n setEnvKeys.push(key)\n }\n\n // Return cleanup function\n return () => {\n // Restore or delete environment variables\n for (const key of setEnvKeys) {\n if (originalEnvValues[key] === undefined) {\n delete process.env[key]\n } else {\n process.env[key] = originalEnvValues[key]\n }\n }\n\n // Clean up browser stubs\n cleanupBrowserStubs()\n }\n}\n"],"names":["getStudioEnvironmentVariables","setupBrowserStubs","mockBrowserEnvironment","basePath","cleanupBrowserStubs","envVars","setEnvKeys","originalEnvValues","key","value","Object","entries","process","env","push","undefined"],"mappings":"AAAA,kEAAkE;AAClE,SAAQA,6BAA6B,QAAO,qCAAoC;AAChF,SAAQC,iBAAiB,QAAO,yBAAwB;AAExD;;;;;;;;;;;;;;;CAeC,GACD,OAAO,eAAeC,uBAAuBC,QAAgB;IAC3D,2BAA2B;IAC3B,MAAMC,sBAAsB,MAAMH;IAElC,yDAAyD;IACzD,MAAMI,UAAU,MAAML,8BAA8BG;IACpD,MAAMG,aAAuB,EAAE;IAC/B,MAAMC,oBAAwD,CAAC;IAE/D,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACN,SAAU;QAClD,qDAAqD;QACrDE,iBAAiB,CAACC,IAAI,GAAGI,QAAQC,GAAG,CAACL,IAAI;QACzCI,QAAQC,GAAG,CAACL,IAAI,GAAGC;QACnBH,WAAWQ,IAAI,CAACN;IAClB;IAEA,0BAA0B;IAC1B,OAAO;QACL,0CAA0C;QAC1C,KAAK,MAAMA,OAAOF,WAAY;YAC5B,IAAIC,iBAAiB,CAACC,IAAI,KAAKO,WAAW;gBACxC,OAAOH,QAAQC,GAAG,CAACL,IAAI;YACzB,OAAO;gBACLI,QAAQC,GAAG,CAACL,IAAI,GAAGD,iBAAiB,CAACC,IAAI;YAC3C;QACF;QAEA,yBAAyB;QACzBJ;IACF;AACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getBrowserStubs } from './stubs.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sets up browser globals (window, document, etc.) in the global scope.
|
|
4
|
+
*
|
|
5
|
+
* This is used by both mockBrowserEnvironment (for child processes) and
|
|
6
|
+
* studioWorkerLoader (for worker threads) to provide a browser-like environment.
|
|
7
|
+
*
|
|
8
|
+
* @returns A cleanup function that removes the injected globals
|
|
9
|
+
* @internal
|
|
10
|
+
*/ export async function setupBrowserStubs() {
|
|
11
|
+
// Guard against double-registering
|
|
12
|
+
if (globalThis.window && '__mockedBySanity' in globalThis.window) {
|
|
13
|
+
return ()=>{
|
|
14
|
+
/* intentional noop - already mocked */ };
|
|
15
|
+
}
|
|
16
|
+
// Inject browser stubs into global scope
|
|
17
|
+
const stubs = getBrowserStubs();
|
|
18
|
+
const mockedGlobalThis = globalThis;
|
|
19
|
+
const stubbedKeys = [];
|
|
20
|
+
for (const key of Object.keys(stubs)){
|
|
21
|
+
if (key in mockedGlobalThis) continue;
|
|
22
|
+
mockedGlobalThis[key] = stubs[key];
|
|
23
|
+
stubbedKeys.push(key);
|
|
24
|
+
}
|
|
25
|
+
// Add marker to window to detect double-mocking
|
|
26
|
+
if (globalThis.window) {
|
|
27
|
+
;
|
|
28
|
+
globalThis.window.__mockedBySanity = true;
|
|
29
|
+
}
|
|
30
|
+
// Return cleanup function
|
|
31
|
+
return ()=>{
|
|
32
|
+
// Remove marker before deleting window
|
|
33
|
+
if (globalThis.window) {
|
|
34
|
+
delete globalThis.window.__mockedBySanity;
|
|
35
|
+
}
|
|
36
|
+
for (const key of stubbedKeys){
|
|
37
|
+
delete mockedGlobalThis[key];
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=setupBrowserStubs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/environment/setupBrowserStubs.ts"],"sourcesContent":["import {getBrowserStubs} from './stubs.js'\n\n/**\n * Sets up browser globals (window, document, etc.) in the global scope.\n *\n * This is used by both mockBrowserEnvironment (for child processes) and\n * studioWorkerLoader (for worker threads) to provide a browser-like environment.\n *\n * @returns A cleanup function that removes the injected globals\n * @internal\n */\nexport async function setupBrowserStubs(): Promise<() => void> {\n // Guard against double-registering\n if (globalThis.window && '__mockedBySanity' in globalThis.window) {\n return () => {\n /* intentional noop - already mocked */\n }\n }\n\n // Inject browser stubs into global scope\n const stubs = getBrowserStubs()\n const mockedGlobalThis: Record<string, unknown> = globalThis\n const stubbedKeys: string[] = []\n\n for (const key of Object.keys(stubs)) {\n if (key in mockedGlobalThis) continue\n mockedGlobalThis[key] = stubs[key]\n stubbedKeys.push(key)\n }\n\n // Add marker to window to detect double-mocking\n if (globalThis.window) {\n ;(globalThis.window as unknown as Record<string, unknown>).__mockedBySanity = true\n }\n\n // Return cleanup function\n return () => {\n // Remove marker before deleting window\n if (globalThis.window) {\n delete (globalThis.window as unknown as Record<string, unknown>).__mockedBySanity\n }\n\n for (const key of stubbedKeys) {\n delete mockedGlobalThis[key]\n }\n }\n}\n"],"names":["getBrowserStubs","setupBrowserStubs","globalThis","window","stubs","mockedGlobalThis","stubbedKeys","key","Object","keys","push","__mockedBySanity"],"mappings":"AAAA,SAAQA,eAAe,QAAO,aAAY;AAE1C;;;;;;;;CAQC,GACD,OAAO,eAAeC;IACpB,mCAAmC;IACnC,IAAIC,WAAWC,MAAM,IAAI,sBAAsBD,WAAWC,MAAM,EAAE;QAChE,OAAO;QACL,qCAAqC,GACvC;IACF;IAEA,yCAAyC;IACzC,MAAMC,QAAQJ;IACd,MAAMK,mBAA4CH;IAClD,MAAMI,cAAwB,EAAE;IAEhC,KAAK,MAAMC,OAAOC,OAAOC,IAAI,CAACL,OAAQ;QACpC,IAAIG,OAAOF,kBAAkB;QAC7BA,gBAAgB,CAACE,IAAI,GAAGH,KAAK,CAACG,IAAI;QAClCD,YAAYI,IAAI,CAACH;IACnB;IAEA,gDAAgD;IAChD,IAAIL,WAAWC,MAAM,EAAE;;QACnBD,WAAWC,MAAM,CAAwCQ,gBAAgB,GAAG;IAChF;IAEA,0BAA0B;IAC1B,OAAO;QACL,uCAAuC;QACvC,IAAIT,WAAWC,MAAM,EAAE;YACrB,OAAO,AAACD,WAAWC,MAAM,CAAwCQ,gBAAgB;QACnF;QAEA,KAAK,MAAMJ,OAAOD,YAAa;YAC7B,OAAOD,gBAAgB,CAACE,IAAI;QAC9B;IACF;AACF"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { JSDOM } from 'jsdom';
|
|
2
|
+
const html = `<!doctype html>
|
|
3
|
+
<html>
|
|
4
|
+
<head><meta charset="utf-8"></head>
|
|
5
|
+
<body></body>
|
|
6
|
+
</html>`;
|
|
7
|
+
function isAbortSignalLike(value) {
|
|
8
|
+
return !!value && typeof value === 'object' && 'aborted' in value && typeof value.aborted === 'boolean' && 'addEventListener' in value && typeof value.addEventListener === 'function' && 'removeEventListener' in value && typeof value.removeEventListener === 'function';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Make JSDOM's `addEventListener({signal})` accept native Node.js AbortSignals.
|
|
12
|
+
*
|
|
13
|
+
* JSDOM validates `signal` using its own realm's `AbortSignal` constructor, which rejects
|
|
14
|
+
* Node's native signal. Instead of replacing the global Abort APIs, intercept those calls
|
|
15
|
+
* and emulate the abortable-listener behavior for cross-realm signals.
|
|
16
|
+
*/ function patchEventTargetSignalSupport(dom) {
|
|
17
|
+
const eventTarget = dom.window.EventTarget;
|
|
18
|
+
const addEventListener = eventTarget.prototype.addEventListener;
|
|
19
|
+
eventTarget.prototype.addEventListener = function addEventListenerPatched(type, listener, options) {
|
|
20
|
+
if (typeof options === 'boolean' || !options?.signal) {
|
|
21
|
+
return addEventListener.call(this, type, listener, options);
|
|
22
|
+
}
|
|
23
|
+
const rawSignal = options.signal;
|
|
24
|
+
const isJSDOMSignal = rawSignal instanceof dom.window.AbortSignal;
|
|
25
|
+
if (!rawSignal || isJSDOMSignal || !isAbortSignalLike(rawSignal)) {
|
|
26
|
+
return addEventListener.call(this, type, listener, options);
|
|
27
|
+
}
|
|
28
|
+
const signal = rawSignal;
|
|
29
|
+
if (signal.aborted) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const { signal: _signal, ...optionsWithoutSignal } = options;
|
|
33
|
+
addEventListener.call(this, type, listener, optionsWithoutSignal);
|
|
34
|
+
const removeOnAbort = ()=>{
|
|
35
|
+
this.removeEventListener(type, listener, options);
|
|
36
|
+
};
|
|
37
|
+
signal.addEventListener('abort', removeOnAbort, {
|
|
38
|
+
once: true
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a JSDOM instance and applies polyfills for missing browser globals.
|
|
44
|
+
*/ function createBrowserDom() {
|
|
45
|
+
const dom = new JSDOM(html, {
|
|
46
|
+
pretendToBeVisual: true,
|
|
47
|
+
url: 'http://localhost:3333/'
|
|
48
|
+
});
|
|
49
|
+
patchEventTargetSignalSupport(dom);
|
|
50
|
+
// Special handling of certain globals
|
|
51
|
+
if (typeof dom.window.document.execCommand !== 'function') {
|
|
52
|
+
// Crashes ace editor without this :/
|
|
53
|
+
dom.window.document.execCommand = function execCommand(// Provide the right arity for the function, even if unused
|
|
54
|
+
_commandName, _showDefaultUI, _valueArgument) {
|
|
55
|
+
// Return false to indicate "unsupported"
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (dom.window.requestIdleCallback === undefined) {
|
|
60
|
+
dom.window.requestIdleCallback = (cb)=>setTimeout(cb, 10);
|
|
61
|
+
}
|
|
62
|
+
if (dom.window.cancelIdleCallback === undefined) {
|
|
63
|
+
dom.window.cancelIdleCallback = (id)=>clearTimeout(id);
|
|
64
|
+
}
|
|
65
|
+
if (dom.window.ResizeObserver === undefined) {
|
|
66
|
+
dom.window.ResizeObserver = class ResizeObserver {
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
68
|
+
constructor(_callback){}
|
|
69
|
+
disconnect() {}
|
|
70
|
+
observe(_target, _options) {}
|
|
71
|
+
unobserve(_target) {}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (dom.window.IntersectionObserver === undefined) {
|
|
75
|
+
dom.window.IntersectionObserver = class IntersectionObserver {
|
|
76
|
+
options;
|
|
77
|
+
constructor(_callback, options){
|
|
78
|
+
this.options = options || {};
|
|
79
|
+
}
|
|
80
|
+
get root() {
|
|
81
|
+
return this.options.root || null;
|
|
82
|
+
}
|
|
83
|
+
get rootMargin() {
|
|
84
|
+
return this.options.rootMargin || '';
|
|
85
|
+
}
|
|
86
|
+
get thresholds() {
|
|
87
|
+
return Array.isArray(this.options.threshold) ? this.options.threshold : [
|
|
88
|
+
this.options.threshold || 0
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
disconnect() {}
|
|
92
|
+
observe(_el) {}
|
|
93
|
+
takeRecords() {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
unobserve(_el) {}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (dom.window.matchMedia === undefined) {
|
|
100
|
+
dom.window.matchMedia = (_qs)=>({
|
|
101
|
+
matches: false,
|
|
102
|
+
media: '',
|
|
103
|
+
onchange: null
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return dom;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Collects all browser globals from the JSDOM window that should be injected
|
|
110
|
+
* into the Node.js global scope to emulate a browser environment.
|
|
111
|
+
*
|
|
112
|
+
* This dynamically iterates over all own properties of the JSDOM window,
|
|
113
|
+
* skipping internal JSDOM properties (prefixed with `_`) and properties that
|
|
114
|
+
* already exist in Node.js globals to avoid conflicts.
|
|
115
|
+
*
|
|
116
|
+
* This approach ensures that any new properties added by JSDOM upgrades are
|
|
117
|
+
* automatically included, preventing "missing global" bugs (e.g. `Element`,
|
|
118
|
+
* `HTMLElement`, `SVGElement` needed by libraries like styled-components).
|
|
119
|
+
*/ function collectBrowserStubs() {
|
|
120
|
+
const dom = createBrowserDom();
|
|
121
|
+
const stubs = Object.create(null);
|
|
122
|
+
const nodeGlobals = new Set(Object.getOwnPropertyNames(globalThis));
|
|
123
|
+
for (const key of Object.getOwnPropertyNames(dom.window)){
|
|
124
|
+
// Skip internal JSDOM properties
|
|
125
|
+
if (key.startsWith('_')) continue;
|
|
126
|
+
// Skip numeric indices (e.g. '0' for window[0])
|
|
127
|
+
if (/^\d+$/.test(key)) continue;
|
|
128
|
+
// Skip properties that Node.js already provides to avoid conflicts
|
|
129
|
+
if (nodeGlobals.has(key)) continue;
|
|
130
|
+
stubs[key] = dom.window[key];
|
|
131
|
+
}
|
|
132
|
+
return stubs;
|
|
133
|
+
}
|
|
134
|
+
let browserStubs;
|
|
135
|
+
export function getBrowserStubs() {
|
|
136
|
+
if (!browserStubs) {
|
|
137
|
+
browserStubs = collectBrowserStubs();
|
|
138
|
+
}
|
|
139
|
+
return browserStubs;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=stubs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/environment/stubs.ts"],"sourcesContent":["import {JSDOM} from 'jsdom'\n\nconst html = `<!doctype html>\n<html>\n <head><meta charset=\"utf-8\"></head>\n <body></body>\n</html>`\n\ninterface AbortSignalLike {\n aborted: boolean\n addEventListener(type: 'abort', listener: () => void, options?: {once?: boolean}): void\n removeEventListener(type: 'abort', listener: () => void): void\n}\n\ntype EventListenerOptionsWithSignal = AddEventListenerOptions & {signal?: unknown}\n\nfunction isAbortSignalLike(value: unknown): value is AbortSignalLike {\n return (\n !!value &&\n typeof value === 'object' &&\n 'aborted' in value &&\n typeof (value as AbortSignalLike).aborted === 'boolean' &&\n 'addEventListener' in value &&\n typeof (value as AbortSignalLike).addEventListener === 'function' &&\n 'removeEventListener' in value &&\n typeof (value as AbortSignalLike).removeEventListener === 'function'\n )\n}\n\n/**\n * Make JSDOM's `addEventListener({signal})` accept native Node.js AbortSignals.\n *\n * JSDOM validates `signal` using its own realm's `AbortSignal` constructor, which rejects\n * Node's native signal. Instead of replacing the global Abort APIs, intercept those calls\n * and emulate the abortable-listener behavior for cross-realm signals.\n */\nfunction patchEventTargetSignalSupport(dom: JSDOM): void {\n const eventTarget = dom.window.EventTarget\n const addEventListener = eventTarget.prototype.addEventListener\n\n eventTarget.prototype.addEventListener = function addEventListenerPatched(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: AddEventListenerOptions | boolean,\n ) {\n if (typeof options === 'boolean' || !options?.signal) {\n return addEventListener.call(this, type, listener, options)\n }\n\n const rawSignal = (options as EventListenerOptionsWithSignal).signal\n const isJSDOMSignal = rawSignal instanceof (dom.window.AbortSignal as typeof AbortSignal)\n if (!rawSignal || isJSDOMSignal || !isAbortSignalLike(rawSignal)) {\n return addEventListener.call(this, type, listener, options)\n }\n\n const signal: AbortSignalLike = rawSignal\n if (signal.aborted) {\n return\n }\n\n const {signal: _signal, ...optionsWithoutSignal} = options as EventListenerOptionsWithSignal\n addEventListener.call(this, type, listener, optionsWithoutSignal)\n\n const removeOnAbort = () => {\n this.removeEventListener(type, listener, options)\n }\n\n signal.addEventListener('abort', removeOnAbort, {once: true})\n }\n}\n\n/**\n * Creates a JSDOM instance and applies polyfills for missing browser globals.\n */\nfunction createBrowserDom(): JSDOM {\n const dom = new JSDOM(html, {\n pretendToBeVisual: true,\n url: 'http://localhost:3333/',\n })\n\n patchEventTargetSignalSupport(dom)\n\n // Special handling of certain globals\n if (typeof dom.window.document.execCommand !== 'function') {\n // Crashes ace editor without this :/\n dom.window.document.execCommand = function execCommand(\n // Provide the right arity for the function, even if unused\n _commandName: string,\n _showDefaultUI: boolean,\n _valueArgument: unknown,\n ) {\n // Return false to indicate \"unsupported\"\n return false\n }\n }\n\n if (dom.window.requestIdleCallback === undefined) {\n dom.window.requestIdleCallback = (cb: IdleRequestCallback) => setTimeout(cb, 10)\n }\n\n if (dom.window.cancelIdleCallback === undefined) {\n dom.window.cancelIdleCallback = (id: number) => clearTimeout(id)\n }\n\n if (dom.window.ResizeObserver === undefined) {\n dom.window.ResizeObserver = class ResizeObserver {\n // eslint-disable-next-line @typescript-eslint/no-useless-constructor\n constructor(_callback: unknown) {}\n disconnect() {}\n observe(_target: unknown, _options: unknown) {}\n unobserve(_target: unknown) {}\n }\n }\n\n if (dom.window.IntersectionObserver === undefined) {\n dom.window.IntersectionObserver = class IntersectionObserver {\n options: {root?: unknown; rootMargin?: string; threshold?: number}\n constructor(\n _callback: unknown,\n options?: {root?: unknown; rootMargin?: string; threshold?: number},\n ) {\n this.options = options || {}\n }\n get root() {\n return this.options.root || null\n }\n get rootMargin() {\n return this.options.rootMargin || ''\n }\n get thresholds() {\n return Array.isArray(this.options.threshold)\n ? this.options.threshold\n : [this.options.threshold || 0]\n }\n\n disconnect() {}\n observe(_el: unknown) {}\n takeRecords() {\n return []\n }\n unobserve(_el: unknown) {}\n }\n }\n\n if (dom.window.matchMedia === undefined) {\n dom.window.matchMedia = (_qs: unknown) =>\n ({\n matches: false,\n media: '',\n onchange: null,\n }) as MediaQueryList\n }\n\n return dom\n}\n\n/**\n * Collects all browser globals from the JSDOM window that should be injected\n * into the Node.js global scope to emulate a browser environment.\n *\n * This dynamically iterates over all own properties of the JSDOM window,\n * skipping internal JSDOM properties (prefixed with `_`) and properties that\n * already exist in Node.js globals to avoid conflicts.\n *\n * This approach ensures that any new properties added by JSDOM upgrades are\n * automatically included, preventing \"missing global\" bugs (e.g. `Element`,\n * `HTMLElement`, `SVGElement` needed by libraries like styled-components).\n */\nfunction collectBrowserStubs(): Record<string, unknown> {\n const dom = createBrowserDom()\n const stubs: Record<string, unknown> = Object.create(null)\n const nodeGlobals = new Set(Object.getOwnPropertyNames(globalThis))\n\n for (const key of Object.getOwnPropertyNames(dom.window)) {\n // Skip internal JSDOM properties\n if (key.startsWith('_')) continue\n\n // Skip numeric indices (e.g. '0' for window[0])\n if (/^\\d+$/.test(key)) continue\n\n // Skip properties that Node.js already provides to avoid conflicts\n if (nodeGlobals.has(key)) continue\n\n stubs[key] = (dom.window as Record<string, unknown>)[key]\n }\n\n return stubs\n}\n\nlet browserStubs: Record<string, unknown> | undefined\n\nexport function getBrowserStubs(): Record<string, unknown> {\n if (!browserStubs) {\n browserStubs = collectBrowserStubs()\n }\n return browserStubs\n}\n"],"names":["JSDOM","html","isAbortSignalLike","value","aborted","addEventListener","removeEventListener","patchEventTargetSignalSupport","dom","eventTarget","window","EventTarget","prototype","addEventListenerPatched","type","listener","options","signal","call","rawSignal","isJSDOMSignal","AbortSignal","_signal","optionsWithoutSignal","removeOnAbort","once","createBrowserDom","pretendToBeVisual","url","document","execCommand","_commandName","_showDefaultUI","_valueArgument","requestIdleCallback","undefined","cb","setTimeout","cancelIdleCallback","id","clearTimeout","ResizeObserver","_callback","disconnect","observe","_target","_options","unobserve","IntersectionObserver","root","rootMargin","thresholds","Array","isArray","threshold","_el","takeRecords","matchMedia","_qs","matches","media","onchange","collectBrowserStubs","stubs","Object","create","nodeGlobals","Set","getOwnPropertyNames","globalThis","key","startsWith","test","has","browserStubs","getBrowserStubs"],"mappings":"AAAA,SAAQA,KAAK,QAAO,QAAO;AAE3B,MAAMC,OAAO,CAAC;;;;OAIP,CAAC;AAUR,SAASC,kBAAkBC,KAAc;IACvC,OACE,CAAC,CAACA,SACF,OAAOA,UAAU,YACjB,aAAaA,SACb,OAAO,AAACA,MAA0BC,OAAO,KAAK,aAC9C,sBAAsBD,SACtB,OAAO,AAACA,MAA0BE,gBAAgB,KAAK,cACvD,yBAAyBF,SACzB,OAAO,AAACA,MAA0BG,mBAAmB,KAAK;AAE9D;AAEA;;;;;;CAMC,GACD,SAASC,8BAA8BC,GAAU;IAC/C,MAAMC,cAAcD,IAAIE,MAAM,CAACC,WAAW;IAC1C,MAAMN,mBAAmBI,YAAYG,SAAS,CAACP,gBAAgB;IAE/DI,YAAYG,SAAS,CAACP,gBAAgB,GAAG,SAASQ,wBAChDC,IAAY,EACZC,QAAmD,EACnDC,OAA2C;QAE3C,IAAI,OAAOA,YAAY,aAAa,CAACA,SAASC,QAAQ;YACpD,OAAOZ,iBAAiBa,IAAI,CAAC,IAAI,EAAEJ,MAAMC,UAAUC;QACrD;QAEA,MAAMG,YAAY,AAACH,QAA2CC,MAAM;QACpE,MAAMG,gBAAgBD,qBAAsBX,IAAIE,MAAM,CAACW,WAAW;QAClE,IAAI,CAACF,aAAaC,iBAAiB,CAAClB,kBAAkBiB,YAAY;YAChE,OAAOd,iBAAiBa,IAAI,CAAC,IAAI,EAAEJ,MAAMC,UAAUC;QACrD;QAEA,MAAMC,SAA0BE;QAChC,IAAIF,OAAOb,OAAO,EAAE;YAClB;QACF;QAEA,MAAM,EAACa,QAAQK,OAAO,EAAE,GAAGC,sBAAqB,GAAGP;QACnDX,iBAAiBa,IAAI,CAAC,IAAI,EAAEJ,MAAMC,UAAUQ;QAE5C,MAAMC,gBAAgB;YACpB,IAAI,CAAClB,mBAAmB,CAACQ,MAAMC,UAAUC;QAC3C;QAEAC,OAAOZ,gBAAgB,CAAC,SAASmB,eAAe;YAACC,MAAM;QAAI;IAC7D;AACF;AAEA;;CAEC,GACD,SAASC;IACP,MAAMlB,MAAM,IAAIR,MAAMC,MAAM;QAC1B0B,mBAAmB;QACnBC,KAAK;IACP;IAEArB,8BAA8BC;IAE9B,sCAAsC;IACtC,IAAI,OAAOA,IAAIE,MAAM,CAACmB,QAAQ,CAACC,WAAW,KAAK,YAAY;QACzD,qCAAqC;QACrCtB,IAAIE,MAAM,CAACmB,QAAQ,CAACC,WAAW,GAAG,SAASA,YACzC,2DAA2D;QAC3DC,YAAoB,EACpBC,cAAuB,EACvBC,cAAuB;YAEvB,yCAAyC;YACzC,OAAO;QACT;IACF;IAEA,IAAIzB,IAAIE,MAAM,CAACwB,mBAAmB,KAAKC,WAAW;QAChD3B,IAAIE,MAAM,CAACwB,mBAAmB,GAAG,CAACE,KAA4BC,WAAWD,IAAI;IAC/E;IAEA,IAAI5B,IAAIE,MAAM,CAAC4B,kBAAkB,KAAKH,WAAW;QAC/C3B,IAAIE,MAAM,CAAC4B,kBAAkB,GAAG,CAACC,KAAeC,aAAaD;IAC/D;IAEA,IAAI/B,IAAIE,MAAM,CAAC+B,cAAc,KAAKN,WAAW;QAC3C3B,IAAIE,MAAM,CAAC+B,cAAc,GAAG,MAAMA;YAChC,qEAAqE;YACrE,YAAYC,SAAkB,CAAE,CAAC;YACjCC,aAAa,CAAC;YACdC,QAAQC,OAAgB,EAAEC,QAAiB,EAAE,CAAC;YAC9CC,UAAUF,OAAgB,EAAE,CAAC;QAC/B;IACF;IAEA,IAAIrC,IAAIE,MAAM,CAACsC,oBAAoB,KAAKb,WAAW;QACjD3B,IAAIE,MAAM,CAACsC,oBAAoB,GAAG,MAAMA;YACtChC,QAAkE;YAClE,YACE0B,SAAkB,EAClB1B,OAAmE,CACnE;gBACA,IAAI,CAACA,OAAO,GAAGA,WAAW,CAAC;YAC7B;YACA,IAAIiC,OAAO;gBACT,OAAO,IAAI,CAACjC,OAAO,CAACiC,IAAI,IAAI;YAC9B;YACA,IAAIC,aAAa;gBACf,OAAO,IAAI,CAAClC,OAAO,CAACkC,UAAU,IAAI;YACpC;YACA,IAAIC,aAAa;gBACf,OAAOC,MAAMC,OAAO,CAAC,IAAI,CAACrC,OAAO,CAACsC,SAAS,IACvC,IAAI,CAACtC,OAAO,CAACsC,SAAS,GACtB;oBAAC,IAAI,CAACtC,OAAO,CAACsC,SAAS,IAAI;iBAAE;YACnC;YAEAX,aAAa,CAAC;YACdC,QAAQW,GAAY,EAAE,CAAC;YACvBC,cAAc;gBACZ,OAAO,EAAE;YACX;YACAT,UAAUQ,GAAY,EAAE,CAAC;QAC3B;IACF;IAEA,IAAI/C,IAAIE,MAAM,CAAC+C,UAAU,KAAKtB,WAAW;QACvC3B,IAAIE,MAAM,CAAC+C,UAAU,GAAG,CAACC,MACtB,CAAA;gBACCC,SAAS;gBACTC,OAAO;gBACPC,UAAU;YACZ,CAAA;IACJ;IAEA,OAAOrD;AACT;AAEA;;;;;;;;;;;CAWC,GACD,SAASsD;IACP,MAAMtD,MAAMkB;IACZ,MAAMqC,QAAiCC,OAAOC,MAAM,CAAC;IACrD,MAAMC,cAAc,IAAIC,IAAIH,OAAOI,mBAAmB,CAACC;IAEvD,KAAK,MAAMC,OAAON,OAAOI,mBAAmB,CAAC5D,IAAIE,MAAM,EAAG;QACxD,iCAAiC;QACjC,IAAI4D,IAAIC,UAAU,CAAC,MAAM;QAEzB,gDAAgD;QAChD,IAAI,QAAQC,IAAI,CAACF,MAAM;QAEvB,mEAAmE;QACnE,IAAIJ,YAAYO,GAAG,CAACH,MAAM;QAE1BP,KAAK,CAACO,IAAI,GAAG,AAAC9D,IAAIE,MAAM,AAA4B,CAAC4D,IAAI;IAC3D;IAEA,OAAOP;AACT;AAEA,IAAIW;AAEJ,OAAO,SAASC;IACd,IAAI,CAACD,cAAc;QACjBA,eAAeZ;IACjB;IACA,OAAOY;AACT"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if a file exists and can be "accessed".
|
|
4
|
+
* Prone to race conditions, but good enough for our use cases.
|
|
5
|
+
*
|
|
6
|
+
* @param filePath - The path to the file to check
|
|
7
|
+
* @returns A promise that resolves to true if the file exists, false otherwise
|
|
8
|
+
* @internal
|
|
9
|
+
*/ export function fileExists(filePath) {
|
|
10
|
+
return access(filePath).then(()=>true, ()=>false);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=fileExists.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/fileExists.ts"],"sourcesContent":["import {access} from 'node:fs/promises'\n\n/**\n * Checks if a file exists and can be \"accessed\".\n * Prone to race conditions, but good enough for our use cases.\n *\n * @param filePath - The path to the file to check\n * @returns A promise that resolves to true if the file exists, false otherwise\n * @internal\n */\nexport function fileExists(filePath: string): Promise<boolean> {\n return access(filePath).then(\n () => true,\n () => false,\n )\n}\n"],"names":["access","fileExists","filePath","then"],"mappings":"AAAA,SAAQA,MAAM,QAAO,mBAAkB;AAEvC;;;;;;;CAOC,GACD,OAAO,SAASC,WAAWC,QAAgB;IACzC,OAAOF,OAAOE,UAAUC,IAAI,CAC1B,IAAM,MACN,IAAM;AAEV"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a help URL for the given slug
|
|
3
|
+
*
|
|
4
|
+
* @param slug - The slug to generate a help URL for
|
|
5
|
+
* @returns The generated help URL
|
|
6
|
+
* @internal
|
|
7
|
+
*/ export function generateHelpUrl(slug) {
|
|
8
|
+
return `https://www.sanity.io/help/${slug}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=generateHelpUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/generateHelpUrl.ts"],"sourcesContent":["/**\n * Generate a help URL for the given slug\n *\n * @param slug - The slug to generate a help URL for\n * @returns The generated help URL\n * @internal\n */\nexport function generateHelpUrl(slug: string): string {\n return `https://www.sanity.io/help/${slug}`\n}\n"],"names":["generateHelpUrl","slug"],"mappings":"AAAA;;;;;;CAMC,GACD,OAAO,SAASA,gBAAgBC,IAAY;IAC1C,OAAO,CAAC,2BAA2B,EAAEA,MAAM;AAC7C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createClient } from '@sanity/client';
|
|
2
|
+
export function getEmptyAuth() {
|
|
3
|
+
return {
|
|
4
|
+
authenticated: false,
|
|
5
|
+
client: createClient({
|
|
6
|
+
apiHost: 'http://localhost',
|
|
7
|
+
apiVersion: '2025-02-01',
|
|
8
|
+
projectId: 'unused',
|
|
9
|
+
requestTagPrefix: 'sanity.cli',
|
|
10
|
+
useCdn: false
|
|
11
|
+
}),
|
|
12
|
+
currentUser: null
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=getEmptyAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/getEmptyAuth.ts"],"sourcesContent":["import {createClient} from '@sanity/client'\n\nexport function getEmptyAuth() {\n return {\n authenticated: false,\n client: createClient({\n apiHost: 'http://localhost',\n apiVersion: '2025-02-01',\n projectId: 'unused',\n requestTagPrefix: 'sanity.cli',\n useCdn: false,\n }),\n currentUser: null,\n }\n}\n"],"names":["createClient","getEmptyAuth","authenticated","client","apiHost","apiVersion","projectId","requestTagPrefix","useCdn","currentUser"],"mappings":"AAAA,SAAQA,YAAY,QAAO,iBAAgB;AAE3C,OAAO,SAASC;IACd,OAAO;QACLC,eAAe;QACfC,QAAQH,aAAa;YACnBI,SAAS;YACTC,YAAY;YACZC,WAAW;YACXC,kBAAkB;YAClBC,QAAQ;QACV;QACAC,aAAa;IACf;AACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets an environment variable with the appropriate Sanity prefix based on whether it's an app or studio.
|
|
3
|
+
*
|
|
4
|
+
* @param suffix - The suffix for the environment variable (e.g., 'SERVER_HOSTNAME')
|
|
5
|
+
* @param isApp - Whether to use the app prefix (SANITY_APP_) or studio prefix (SANITY_STUDIO_)
|
|
6
|
+
* @returns The value of the environment variable, or undefined if not set
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // For studio: SANITY_STUDIO_SERVER_HOSTNAME
|
|
11
|
+
* const studioHostname = getSanityEnvVar('SERVER_HOSTNAME', false)
|
|
12
|
+
*
|
|
13
|
+
* // For app: SANITY_APP_SERVER_HOSTNAME
|
|
14
|
+
* const appHostname = getSanityEnvVar('SERVER_HOSTNAME', true)
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/ export function getSanityEnvVar(suffix, isApp) {
|
|
19
|
+
const prefix = isApp ? 'SANITY_APP_' : 'SANITY_STUDIO_';
|
|
20
|
+
const envVarName = `${prefix}${suffix}`;
|
|
21
|
+
return process.env[envVarName];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=getSanityEnvVar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/getSanityEnvVar.ts"],"sourcesContent":["/**\n * Gets an environment variable with the appropriate Sanity prefix based on whether it's an app or studio.\n *\n * @param suffix - The suffix for the environment variable (e.g., 'SERVER_HOSTNAME')\n * @param isApp - Whether to use the app prefix (SANITY_APP_) or studio prefix (SANITY_STUDIO_)\n * @returns The value of the environment variable, or undefined if not set\n *\n * @example\n * ```ts\n * // For studio: SANITY_STUDIO_SERVER_HOSTNAME\n * const studioHostname = getSanityEnvVar('SERVER_HOSTNAME', false)\n *\n * // For app: SANITY_APP_SERVER_HOSTNAME\n * const appHostname = getSanityEnvVar('SERVER_HOSTNAME', true)\n * ```\n *\n * @internal\n */\nexport function getSanityEnvVar(suffix: string, isApp: boolean): string | undefined {\n const prefix = isApp ? 'SANITY_APP_' : 'SANITY_STUDIO_'\n const envVarName = `${prefix}${suffix}`\n return process.env[envVarName]\n}\n"],"names":["getSanityEnvVar","suffix","isApp","prefix","envVarName","process","env"],"mappings":"AAAA;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,SAASA,gBAAgBC,MAAc,EAAEC,KAAc;IAC5D,MAAMC,SAASD,QAAQ,gBAAgB;IACvC,MAAME,aAAa,GAAGD,SAASF,QAAQ;IACvC,OAAOI,QAAQC,GAAG,CAACF,WAAW;AAChC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
* @returns The Sanity URL for the given path, using the correct domain based on the environment
|
|
4
|
+
*/ export function getSanityUrl(path = '/') {
|
|
5
|
+
const domain = process.env.SANITY_INTERNAL_ENV === 'staging' ? 'https://www.sanity.work' : 'https://www.sanity.io';
|
|
6
|
+
return `${domain}${path.startsWith('/') ? path : `/${path}`}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=getSanityUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/getSanityUrl.ts"],"sourcesContent":["/**\n * @internal\n * @returns The Sanity URL for the given path, using the correct domain based on the environment\n */\nexport function getSanityUrl(path = '/') {\n const domain =\n process.env.SANITY_INTERNAL_ENV === 'staging'\n ? 'https://www.sanity.work'\n : 'https://www.sanity.io'\n return `${domain}${path.startsWith('/') ? path : `/${path}`}`\n}\n"],"names":["getSanityUrl","path","domain","process","env","SANITY_INTERNAL_ENV","startsWith"],"mappings":"AAAA;;;CAGC,GACD,OAAO,SAASA,aAAaC,OAAO,GAAG;IACrC,MAAMC,SACJC,QAAQC,GAAG,CAACC,mBAAmB,KAAK,YAChC,4BACA;IACN,OAAO,GAAGH,SAASD,KAAKK,UAAU,CAAC,OAAOL,OAAO,CAAC,CAAC,EAAEA,MAAM,EAAE;AAC/D"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
2
|
+
import { createJiti } from '@rexxars/jiti';
|
|
3
|
+
import { subdebug } from '../debug.js';
|
|
4
|
+
const debug = subdebug('importModule');
|
|
5
|
+
/**
|
|
6
|
+
* Imports a module using jiti and returns its exports.
|
|
7
|
+
* This is a thin wrapper around tsx to allow swapping out the underlying implementation in the future if needed.
|
|
8
|
+
*
|
|
9
|
+
* @param filePath - Path to the module to import.
|
|
10
|
+
* @param options - Options for the importModule function.
|
|
11
|
+
* @returns The exported module.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/ export async function importModule(filePath, options = {}) {
|
|
15
|
+
const { default: returnDefault = true, tsconfigPath } = options;
|
|
16
|
+
const jiti = createJiti(import.meta.url, {
|
|
17
|
+
debug: debug.enabled,
|
|
18
|
+
tsconfigPaths: typeof tsconfigPath === 'string' ? tsconfigPath : true
|
|
19
|
+
});
|
|
20
|
+
const fileURL = typeof filePath === 'string' ? pathToFileURL(filePath) : filePath;
|
|
21
|
+
debug(`Loading module: ${fileURLToPath(fileURL)}`, {
|
|
22
|
+
tsconfigPath
|
|
23
|
+
});
|
|
24
|
+
const jitiOptions = {};
|
|
25
|
+
// If the default option is true, add it to the jiti options
|
|
26
|
+
if (returnDefault) {
|
|
27
|
+
jitiOptions.default = true;
|
|
28
|
+
}
|
|
29
|
+
return jiti.import(fileURLToPath(fileURL), jitiOptions);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=importModule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/importModule.ts"],"sourcesContent":["import {fileURLToPath, pathToFileURL} from 'node:url'\n\nimport {createJiti, type JitiResolveOptions} from '@rexxars/jiti'\n\nimport {subdebug} from '../debug.js'\n\ninterface ImportModuleOptions {\n /**\n * Whether to return the default export of the module.\n * Default: true\n */\n default?: boolean\n\n /**\n * Path to the tsconfig file to use for the import. If not provided, the tsconfig\n * will be inferred from the nearest `tsconfig.json` file.\n */\n tsconfigPath?: string\n}\n\nconst debug = subdebug('importModule')\n\n/**\n * Imports a module using jiti and returns its exports.\n * This is a thin wrapper around tsx to allow swapping out the underlying implementation in the future if needed.\n *\n * @param filePath - Path to the module to import.\n * @param options - Options for the importModule function.\n * @returns The exported module.\n *\n * @internal\n */\nexport async function importModule<T = unknown>(\n filePath: string | URL,\n options: ImportModuleOptions = {},\n): Promise<T> {\n const {default: returnDefault = true, tsconfigPath} = options\n\n const jiti = createJiti(import.meta.url, {\n debug: debug.enabled,\n tsconfigPaths: typeof tsconfigPath === 'string' ? tsconfigPath : true,\n })\n\n const fileURL = typeof filePath === 'string' ? pathToFileURL(filePath) : filePath\n\n debug(`Loading module: ${fileURLToPath(fileURL)}`, {tsconfigPath})\n\n const jitiOptions: JitiResolveOptions & {default?: true} = {}\n\n // If the default option is true, add it to the jiti options\n if (returnDefault) {\n jitiOptions.default = true\n }\n\n return jiti.import<T>(fileURLToPath(fileURL), jitiOptions)\n}\n"],"names":["fileURLToPath","pathToFileURL","createJiti","subdebug","debug","importModule","filePath","options","default","returnDefault","tsconfigPath","jiti","url","enabled","tsconfigPaths","fileURL","jitiOptions","import"],"mappings":"AAAA,SAAQA,aAAa,EAAEC,aAAa,QAAO,WAAU;AAErD,SAAQC,UAAU,QAAgC,gBAAe;AAEjE,SAAQC,QAAQ,QAAO,cAAa;AAgBpC,MAAMC,QAAQD,SAAS;AAEvB;;;;;;;;;CASC,GACD,OAAO,eAAeE,aACpBC,QAAsB,EACtBC,UAA+B,CAAC,CAAC;IAEjC,MAAM,EAACC,SAASC,gBAAgB,IAAI,EAAEC,YAAY,EAAC,GAAGH;IAEtD,MAAMI,OAAOT,WAAW,YAAYU,GAAG,EAAE;QACvCR,OAAOA,MAAMS,OAAO;QACpBC,eAAe,OAAOJ,iBAAiB,WAAWA,eAAe;IACnE;IAEA,MAAMK,UAAU,OAAOT,aAAa,WAAWL,cAAcK,YAAYA;IAEzEF,MAAM,CAAC,gBAAgB,EAAEJ,cAAce,UAAU,EAAE;QAACL;IAAY;IAEhE,MAAMM,cAAqD,CAAC;IAE5D,4DAA4D;IAC5D,IAAIP,eAAe;QACjBO,YAAYR,OAAO,GAAG;IACxB;IAEA,OAAOG,KAAKM,MAAM,CAAIjB,cAAce,UAAUC;AAChD"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { isTrueish } from './isTrueish.js';
|
|
2
|
+
export const isCi = ()=>isTrueish(process.env.CI) || // Travis CI, CircleCI, Gitlab CI, Appveyor, CodeShip
|
|
3
|
+
isTrueish(process.env.CONTINUOUS_INTEGRATION) || // Travis CI
|
|
4
|
+
isTrueish(process.env.BUILD_NUMBER) // Jenkins, TeamCity
|
|
5
|
+
;
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=isCi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/isCi.ts"],"sourcesContent":["import {isTrueish} from './isTrueish.js'\n\nexport const isCi = (): boolean =>\n isTrueish(process.env.CI) || // Travis CI, CircleCI, Gitlab CI, Appveyor, CodeShip\n isTrueish(process.env.CONTINUOUS_INTEGRATION) || // Travis CI\n isTrueish(process.env.BUILD_NUMBER) // Jenkins, TeamCity\n"],"names":["isTrueish","isCi","process","env","CI","CONTINUOUS_INTEGRATION","BUILD_NUMBER"],"mappings":"AAAA,SAAQA,SAAS,QAAO,iBAAgB;AAExC,OAAO,MAAMC,OAAO,IAClBD,UAAUE,QAAQC,GAAG,CAACC,EAAE,KAAK,qDAAqD;IAClFJ,UAAUE,QAAQC,GAAG,CAACE,sBAAsB,KAAK,YAAY;IAC7DL,UAAUE,QAAQC,GAAG,CAACG,YAAY,EAAE,oBAAoB;CAArB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/isInteractive.ts"],"sourcesContent":["export function isInteractive(): boolean {\n return Boolean(process.stdin.isTTY) && process.env.TERM !== 'dumb' && !('CI' in process.env)\n}\n"],"names":["isInteractive","Boolean","process","stdin","isTTY","env","TERM"],"mappings":"AAAA,OAAO,SAASA;IACd,OAAOC,QAAQC,QAAQC,KAAK,CAACC,KAAK,KAAKF,QAAQG,GAAG,CAACC,IAAI,KAAK,UAAU,CAAE,CAAA,QAAQJ,QAAQG,GAAG,AAAD;AAC5F"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the given value is a record (javascript objectish)
|
|
3
|
+
*
|
|
4
|
+
* @param value - Value to check
|
|
5
|
+
* @returns True if the value is a record, false otherwise
|
|
6
|
+
* @internal
|
|
7
|
+
*/ export function isRecord(value) {
|
|
8
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=isRecord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/isRecord.ts"],"sourcesContent":["/**\n * Checks if the given value is a record (javascript objectish)\n *\n * @param value - Value to check\n * @returns True if the value is a record, false otherwise\n * @internal\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n"],"names":["isRecord","value","Array","isArray"],"mappings":"AAAA;;;;;;CAMC,GACD,OAAO,SAASA,SAASC,KAAc;IACrC,OAAO,OAAOA,UAAU,YAAYA,UAAU,QAAQ,CAACC,MAAMC,OAAO,CAACF;AACvE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the environment is staging.
|
|
3
|
+
*
|
|
4
|
+
* @returns True if the environment is staging, false otherwise
|
|
5
|
+
* @internal
|
|
6
|
+
*/ export function isStaging() {
|
|
7
|
+
return process.env.SANITY_INTERNAL_ENV === 'staging';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=isStaging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/isStaging.ts"],"sourcesContent":["/**\n * Checks if the environment is staging.\n *\n * @returns True if the environment is staging, false otherwise\n * @internal\n */\nexport function isStaging(): boolean {\n return process.env.SANITY_INTERNAL_ENV === 'staging'\n}\n"],"names":["isStaging","process","env","SANITY_INTERNAL_ENV"],"mappings":"AAAA;;;;;CAKC,GACD,OAAO,SAASA;IACd,OAAOC,QAAQC,GAAG,CAACC,mBAAmB,KAAK;AAC7C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function isTrueish(value) {
|
|
2
|
+
if (value === undefined) return false;
|
|
3
|
+
if (value.toLowerCase() === 'true') return true;
|
|
4
|
+
if (value.toLowerCase() === 'false') return false;
|
|
5
|
+
const number = Number.parseInt(value, 10);
|
|
6
|
+
if (Number.isNaN(number)) return false;
|
|
7
|
+
return number > 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=isTrueish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/isTrueish.ts"],"sourcesContent":["export function isTrueish(value: string | undefined) {\n if (value === undefined) return false\n if (value.toLowerCase() === 'true') return true\n if (value.toLowerCase() === 'false') return false\n const number = Number.parseInt(value, 10)\n if (Number.isNaN(number)) return false\n return number > 0\n}\n"],"names":["isTrueish","value","undefined","toLowerCase","number","Number","parseInt","isNaN"],"mappings":"AAAA,OAAO,SAASA,UAAUC,KAAyB;IACjD,IAAIA,UAAUC,WAAW,OAAO;IAChC,IAAID,MAAME,WAAW,OAAO,QAAQ,OAAO;IAC3C,IAAIF,MAAME,WAAW,OAAO,SAAS,OAAO;IAC5C,MAAMC,SAASC,OAAOC,QAAQ,CAACL,OAAO;IACtC,IAAII,OAAOE,KAAK,CAACH,SAAS,OAAO;IACjC,OAAOA,SAAS;AAClB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes a path for cross-platform comparison by converting backslashes to forward slashes.
|
|
3
|
+
* Useful for converting windows paths to unix paths.
|
|
4
|
+
*
|
|
5
|
+
* @param path - Path to normalize
|
|
6
|
+
* @returns Normalized path with forward slashes
|
|
7
|
+
* @public
|
|
8
|
+
*/ export function normalizePath(path) {
|
|
9
|
+
return path.replaceAll('\\', '/');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=normalizePath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/normalizePath.ts"],"sourcesContent":["/**\n * Normalizes a path for cross-platform comparison by converting backslashes to forward slashes.\n * Useful for converting windows paths to unix paths.\n *\n * @param path - Path to normalize\n * @returns Normalized path with forward slashes\n * @public\n */\nexport function normalizePath(path: string): string {\n return path.replaceAll('\\\\', '/')\n}\n"],"names":["normalizePath","path","replaceAll"],"mappings":"AAAA;;;;;;;CAOC,GACD,OAAO,SAASA,cAAcC,IAAY;IACxC,OAAOA,KAAKC,UAAU,CAAC,MAAM;AAC/B"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-detectable package managers (excludes 'manual' which is a UI-only choice).
|
|
3
|
+
*/ /**
|
|
4
|
+
* Convenience wrapper that reads `process.env.npm_config_user_agent` and returns
|
|
5
|
+
* the detected package manager.
|
|
6
|
+
*/ export function getRunningPackageManager() {
|
|
7
|
+
return detectPackageManagerFromAgent();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extract the yarn major version from a `npm_config_user_agent` string.
|
|
11
|
+
*
|
|
12
|
+
* @param ua - User-agent string. Defaults to `process.env.npm_config_user_agent`.
|
|
13
|
+
* @returns The major version number, or `undefined` if yarn isn't detected.
|
|
14
|
+
*/ export function getYarnMajorVersion(ua = process.env.npm_config_user_agent ?? '') {
|
|
15
|
+
const match = ua.match(/yarn\/(\d+)/);
|
|
16
|
+
return match ? Number.parseInt(match[1], 10) : undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Return the CLI invocation command for the detected (or provided) package manager.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Optional `bin` (defaults to `'sanity'`) and `userAgent` override.
|
|
22
|
+
* @returns A string like `"npx sanity"`, `"pnpm exec sanity"`, etc.
|
|
23
|
+
*/ export function getBinCommand(options) {
|
|
24
|
+
const bin = options?.bin ?? 'sanity';
|
|
25
|
+
const ua = options?.userAgent ?? process.env.npm_config_user_agent ?? '';
|
|
26
|
+
const pm = detectPackageManagerFromAgent(ua);
|
|
27
|
+
if (pm === 'npm') return `npx ${bin}`;
|
|
28
|
+
if (pm === 'pnpm') return `pnpm exec ${bin}`;
|
|
29
|
+
if (pm === 'bun') return `bunx ${bin}`;
|
|
30
|
+
if (pm === 'yarn') {
|
|
31
|
+
const major = getYarnMajorVersion(ua);
|
|
32
|
+
if (major !== undefined && major >= 2) return `yarn run ${bin}`;
|
|
33
|
+
return `yarn ${bin}`;
|
|
34
|
+
}
|
|
35
|
+
return bin;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Parse the `npm_config_user_agent` string and return the detected package manager.
|
|
39
|
+
*
|
|
40
|
+
* The check order matters: yarn and pnpm set a user-agent that *also* contains
|
|
41
|
+
* `npm/?`, so we must test for them before falling back to the anchored npm regex.
|
|
42
|
+
*
|
|
43
|
+
* @param ua - User-agent string. Defaults to `process.env.npm_config_user_agent`.
|
|
44
|
+
* @returns The detected package manager, or `undefined` when unrecognisable.
|
|
45
|
+
*/ export function detectPackageManagerFromAgent(ua = process.env.npm_config_user_agent ?? '') {
|
|
46
|
+
if (ua.includes('pnpm')) return 'pnpm';
|
|
47
|
+
if (ua.includes('yarn')) return 'yarn';
|
|
48
|
+
if (ua.includes('bun')) return 'bun';
|
|
49
|
+
// Anchored regex: yarn/pnpm/bun agents also contain "npm/?" so we require
|
|
50
|
+
// `npm/` followed by a digit to avoid false positives.
|
|
51
|
+
if (/^npm\/\d/.test(ua)) return 'npm';
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=packageManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/packageManager.ts"],"sourcesContent":["/**\n * Runtime-detectable package managers (excludes 'manual' which is a UI-only choice).\n */\nexport type DetectedPackageManager = 'bun' | 'npm' | 'pnpm' | 'yarn'\n\n/**\n * Convenience wrapper that reads `process.env.npm_config_user_agent` and returns\n * the detected package manager.\n */\nexport function getRunningPackageManager(): DetectedPackageManager | undefined {\n return detectPackageManagerFromAgent()\n}\n\n/**\n * Extract the yarn major version from a `npm_config_user_agent` string.\n *\n * @param ua - User-agent string. Defaults to `process.env.npm_config_user_agent`.\n * @returns The major version number, or `undefined` if yarn isn't detected.\n */\nexport function getYarnMajorVersion(\n ua: string = process.env.npm_config_user_agent ?? '',\n): number | undefined {\n const match = ua.match(/yarn\\/(\\d+)/)\n return match ? Number.parseInt(match[1], 10) : undefined\n}\n\n/**\n * Return the CLI invocation command for the detected (or provided) package manager.\n *\n * @param options - Optional `bin` (defaults to `'sanity'`) and `userAgent` override.\n * @returns A string like `\"npx sanity\"`, `\"pnpm exec sanity\"`, etc.\n */\nexport function getBinCommand(options?: {bin?: string; userAgent?: string}): string {\n const bin = options?.bin ?? 'sanity'\n const ua = options?.userAgent ?? process.env.npm_config_user_agent ?? ''\n const pm = detectPackageManagerFromAgent(ua)\n\n if (pm === 'npm') return `npx ${bin}`\n if (pm === 'pnpm') return `pnpm exec ${bin}`\n if (pm === 'bun') return `bunx ${bin}`\n if (pm === 'yarn') {\n const major = getYarnMajorVersion(ua)\n if (major !== undefined && major >= 2) return `yarn run ${bin}`\n return `yarn ${bin}`\n }\n return bin\n}\n\n/**\n * Parse the `npm_config_user_agent` string and return the detected package manager.\n *\n * The check order matters: yarn and pnpm set a user-agent that *also* contains\n * `npm/?`, so we must test for them before falling back to the anchored npm regex.\n *\n * @param ua - User-agent string. Defaults to `process.env.npm_config_user_agent`.\n * @returns The detected package manager, or `undefined` when unrecognisable.\n */\nexport function detectPackageManagerFromAgent(\n ua: string = process.env.npm_config_user_agent ?? '',\n): DetectedPackageManager | undefined {\n if (ua.includes('pnpm')) return 'pnpm'\n if (ua.includes('yarn')) return 'yarn'\n if (ua.includes('bun')) return 'bun'\n // Anchored regex: yarn/pnpm/bun agents also contain \"npm/?\" so we require\n // `npm/` followed by a digit to avoid false positives.\n if (/^npm\\/\\d/.test(ua)) return 'npm'\n return undefined\n}\n"],"names":["getRunningPackageManager","detectPackageManagerFromAgent","getYarnMajorVersion","ua","process","env","npm_config_user_agent","match","Number","parseInt","undefined","getBinCommand","options","bin","userAgent","pm","major","includes","test"],"mappings":"AAAA;;CAEC,GAGD;;;CAGC,GACD,OAAO,SAASA;IACd,OAAOC;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASC,oBACdC,KAAaC,QAAQC,GAAG,CAACC,qBAAqB,IAAI,EAAE;IAEpD,MAAMC,QAAQJ,GAAGI,KAAK,CAAC;IACvB,OAAOA,QAAQC,OAAOC,QAAQ,CAACF,KAAK,CAAC,EAAE,EAAE,MAAMG;AACjD;AAEA;;;;;CAKC,GACD,OAAO,SAASC,cAAcC,OAA4C;IACxE,MAAMC,MAAMD,SAASC,OAAO;IAC5B,MAAMV,KAAKS,SAASE,aAAaV,QAAQC,GAAG,CAACC,qBAAqB,IAAI;IACtE,MAAMS,KAAKd,8BAA8BE;IAEzC,IAAIY,OAAO,OAAO,OAAO,CAAC,IAAI,EAAEF,KAAK;IACrC,IAAIE,OAAO,QAAQ,OAAO,CAAC,UAAU,EAAEF,KAAK;IAC5C,IAAIE,OAAO,OAAO,OAAO,CAAC,KAAK,EAAEF,KAAK;IACtC,IAAIE,OAAO,QAAQ;QACjB,MAAMC,QAAQd,oBAAoBC;QAClC,IAAIa,UAAUN,aAAaM,SAAS,GAAG,OAAO,CAAC,SAAS,EAAEH,KAAK;QAC/D,OAAO,CAAC,KAAK,EAAEA,KAAK;IACtB;IACA,OAAOA;AACT;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASZ,8BACdE,KAAaC,QAAQC,GAAG,CAACC,qBAAqB,IAAI,EAAE;IAEpD,IAAIH,GAAGc,QAAQ,CAAC,SAAS,OAAO;IAChC,IAAId,GAAGc,QAAQ,CAAC,SAAS,OAAO;IAChC,IAAId,GAAGc,QAAQ,CAAC,QAAQ,OAAO;IAC/B,0EAA0E;IAC1E,uDAAuD;IACvD,IAAI,WAAWC,IAAI,CAACf,KAAK,OAAO;IAChC,OAAOO;AACT"}
|