diff --git a/.yarn/sdks/eslint/lib/unsupported-api.js b/.yarn/sdks/eslint/lib/unsupported-api.js new file mode 100644 index 000000000000..30fdf158b475 --- /dev/null +++ b/.yarn/sdks/eslint/lib/unsupported-api.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire} = require(`module`); +const {resolve} = require(`path`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absRequire = createRequire(absPnpApiPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/use-at-your-own-risk + require(absPnpApiPath).setup(); + } +} + +// Defer to the real eslint/use-at-your-own-risk your application uses +module.exports = absRequire(`eslint/use-at-your-own-risk`); diff --git a/.yarn/sdks/eslint/package.json b/.yarn/sdks/eslint/package.json index cea14d04b47e..8d17034e6015 100644 --- a/.yarn/sdks/eslint/package.json +++ b/.yarn/sdks/eslint/package.json @@ -2,5 +2,13 @@ "name": "eslint", "version": "8.45.0-sdk", "main": "./lib/api.js", - "type": "commonjs" + "type": "commonjs", + "bin": { + "eslint": "./bin/eslint.js" + }, + "exports": { + "./package.json": "./package.json", + ".": "./lib/api.js", + "./use-at-your-own-risk": "./lib/unsupported-api.js" + } } diff --git a/.yarn/sdks/typescript/lib/typescript.js b/.yarn/sdks/typescript/lib/typescript.js index e14fa87beaa4..b5f4db25bee6 100644 --- a/.yarn/sdks/typescript/lib/typescript.js +++ b/.yarn/sdks/typescript/lib/typescript.js @@ -11,10 +11,10 @@ const absRequire = createRequire(absPnpApiPath); if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { - // Setup the environment to be able to require typescript/lib/typescript.js + // Setup the environment to be able to require typescript require(absPnpApiPath).setup(); } } -// Defer to the real typescript/lib/typescript.js your application uses -module.exports = absRequire(`typescript/lib/typescript.js`); +// Defer to the real typescript your application uses +module.exports = absRequire(`typescript`); diff --git a/.yarn/sdks/typescript/package.json b/.yarn/sdks/typescript/package.json index 2436a93d2bfe..9f56785acb53 100644 --- a/.yarn/sdks/typescript/package.json +++ b/.yarn/sdks/typescript/package.json @@ -2,5 +2,9 @@ "name": "typescript", "version": "5.3.0-beta-sdk", "main": "./lib/typescript.js", - "type": "commonjs" + "type": "commonjs", + "bin": { + "tsc": "./bin/tsc", + "tsserver": "./bin/tsserver" + } } diff --git a/.yarn/versions/4d2d8afa.yml b/.yarn/versions/4d2d8afa.yml new file mode 100644 index 000000000000..99c2d8e32380 --- /dev/null +++ b/.yarn/versions/4d2d8afa.yml @@ -0,0 +1,2 @@ +releases: + "@yarnpkg/sdks": minor diff --git a/packages/acceptance-tests/pkg-tests-specs/sources/features/editorSdks.test.js b/packages/acceptance-tests/pkg-tests-specs/sources/features/editorSdks.test.js index 5be9b09d1c0c..e6ed668db6d1 100644 --- a/packages/acceptance-tests/pkg-tests-specs/sources/features/editorSdks.test.js +++ b/packages/acceptance-tests/pkg-tests-specs/sources/features/editorSdks.test.js @@ -21,6 +21,7 @@ describe(`Features`, () => { await xfs.writeJsonPromise(manifestPath, { name: `eslint`, version: `1.0.0`, + bin: `./bin/eslint.js`, dependencies: { [`no-deps`]: `1.0.0`, }, @@ -58,6 +59,7 @@ describe(`Features`, () => { await xfs.writeJsonPromise(manifestPath, { name: `eslint`, version: `1.0.0`, + bin: `./bin/eslint.js`, dependencies: { [`no-deps`]: `1.0.0`, }, diff --git a/packages/yarnpkg-sdks/sources/commands/SdkCommand.ts b/packages/yarnpkg-sdks/sources/commands/SdkCommand.ts index 55d4f54679e6..0492f1f4620f 100644 --- a/packages/yarnpkg-sdks/sources/commands/SdkCommand.ts +++ b/packages/yarnpkg-sdks/sources/commands/SdkCommand.ts @@ -79,7 +79,7 @@ export default class SdkCommand extends Command { } if (nextProjectRoot === currProjectRoot) - throw new Error(`This tool can only be used with projects using Yarn Plug'n'Play`); + throw new UsageError(`This tool can only be used with projects using Yarn Plug'n'Play`); const configuration = Configuration.create(currProjectRoot); const pnpPath = ppath.join(currProjectRoot, pnpFilename); diff --git a/packages/yarnpkg-sdks/sources/generateSdk.ts b/packages/yarnpkg-sdks/sources/generateSdk.ts index d8339973d81c..3b15c56bca54 100644 --- a/packages/yarnpkg-sdks/sources/generateSdk.ts +++ b/packages/yarnpkg-sdks/sources/generateSdk.ts @@ -184,13 +184,22 @@ export type SupportedSdk = | 'svelte-language-server' | 'flow-bin'; -export type BaseSdks = Array<[SupportedSdk, GenerateBaseWrapper]>; +export type BaseSdks = Array<[ + SupportedSdk, + GenerateBaseWrapper, +]>; export type IntegrationSdks = Array< | [null, GenerateDefaultWrapper | null] | [SupportedSdk, GenerateIntegrationWrapper | null] >; +export type PackageExports = + | {[key: string]: PackageExports} + | Array + | string + | null; + export class Wrapper { private name: PortablePath; @@ -199,16 +208,19 @@ export class Wrapper { private paths: Map = new Map(); - constructor(name: PortablePath, {pnpApi, target}: {pnpApi: PnpApi, target: PortablePath}) { + public readonly manifest: Record; + + constructor(name: PortablePath, {pnpApi, target, manifestOverrides = {}}: {pnpApi: PnpApi, target: PortablePath, manifestOverrides?: Record}) { this.name = name; this.pnpApi = pnpApi; this.target = target; - } - async writeManifest(rawManifest: Record = {}) { - const absWrapperPath = ppath.join(this.target, this.name, `package.json`); + this.manifest = this.loadManifest(); + Object.assign(this.manifest, manifestOverrides); + } + private loadManifest() { const topLevelInformation = this.pnpApi.getPackageInformation(this.pnpApi.topLevel)!; const dependencyReference = topLevelInformation.packageDependencies.get(this.name)!; @@ -216,16 +228,75 @@ export class Wrapper { if (pkgInformation === null) throw new Error(`Assertion failed: Package ${this.name} isn't a dependency of the top-level`); - const manifest = dynamicRequire(npath.join(pkgInformation.packageLocation, `package.json`)); + const manifestPath = npath.join(pkgInformation.packageLocation, Filename.manifest); + const manifest = dynamicRequire(manifestPath); - await xfs.mkdirPromise(ppath.dirname(absWrapperPath), {recursive: true}); - await xfs.writeJsonPromise(absWrapperPath, { + return { name: this.name, version: `${manifest.version}-sdk`, main: manifest.main, type: `commonjs`, - ...rawManifest, - }); + bin: manifest.bin, + exports: manifest.exports, + }; + } + + async writeDefaults() { + if (this.manifest.main) + await this.writeFile(ppath.normalize(this.manifest.main as PortablePath), {requirePath: PortablePath.dot}); + + if (this.manifest.exports) + await this.writePackageExports(); + + if (this.manifest.bin) + await this.writePackageBinaries(); + + await this.writeManifest(); + } + + async writePackageBinaries() { + if (typeof this.manifest.bin === `string`) { + await this.writeBinary(ppath.normalize(this.manifest.bin as PortablePath)); + } else { + for (const relPackagePath of Object.values(this.manifest.bin)) { + await this.writeBinary(ppath.normalize(relPackagePath as PortablePath)); + } + } + } + + async writePackageExports(packageExports: PackageExports = this.manifest.exports, requirePath = PortablePath.dot) { + if (typeof packageExports === `string`) { + if (!packageExports.includes(`*`)) { + await this.writeFile(ppath.normalize(packageExports as PortablePath), {requirePath}); + } + } else if (Array.isArray(packageExports)) { + await Promise.all( + packageExports.map(packageExport => this.writePackageExports(packageExport, requirePath)), + ); + } else if (packageExports !== null) { + await Promise.all( + Object.entries(packageExports).map(async ([key, value]) => { + if (key.startsWith(`.`)) { + await this.writePackageExports(value, key as PortablePath); + } else { + await this.writePackageExports(value, requirePath); + } + }), + ); + } + } + + async writeManifest() { + const topLevelInformation = this.pnpApi.getPackageInformation(this.pnpApi.topLevel)!; + const projectRoot = npath.toPortablePath(topLevelInformation.packageLocation); + + const absWrapperPath = ppath.join(this.target, this.name, `package.json`); + const relProjectPath = ppath.relative(projectRoot, absWrapperPath); + + await xfs.mkdirPromise(ppath.dirname(absWrapperPath), {recursive: true}); + await xfs.writeJsonPromise(absWrapperPath, this.manifest); + + this.paths.set(Filename.manifest, relProjectPath); } async writeBinary(relPackagePath: PortablePath, options: TemplateOptions & {requirePath?: PortablePath} = {}) { @@ -242,10 +313,11 @@ export class Wrapper { const absPnpApiPath = npath.toPortablePath(this.pnpApi.resolveRequest(`pnpapi`, null)!); const relPnpApiPath = ppath.relative(ppath.dirname(absWrapperPath), absPnpApiPath); + const moduleReqPath = ppath.join(this.name, options.requirePath ?? relPackagePath); + const wrapperScript = TEMPLATE(relPnpApiPath, moduleReqPath, options); + await xfs.mkdirPromise(ppath.dirname(absWrapperPath), {recursive: true}); - await xfs.writeFilePromise(absWrapperPath, TEMPLATE(relPnpApiPath, ppath.join(this.name, options.requirePath ?? relPackagePath), options), { - mode: options.mode, - }); + await xfs.writeFilePromise(absWrapperPath, wrapperScript, {mode: options.mode}); this.paths.set(relPackagePath, relProjectPath); diff --git a/packages/yarnpkg-sdks/sources/sdks/base.ts b/packages/yarnpkg-sdks/sources/sdks/base.ts index 7039d49bdb8e..86020c64a966 100644 --- a/packages/yarnpkg-sdks/sources/sdks/base.ts +++ b/packages/yarnpkg-sdks/sources/sdks/base.ts @@ -6,9 +6,7 @@ import {Wrapper, GenerateBaseWrapper, BaseSdks} from '../generateSdk'; export const generateAstroLanguageServerBaseWrapper: GenerateBaseWrapper = async (pnpApi: PnpApi, target: PortablePath) => { const wrapper = new Wrapper(`@astrojs/language-server` as PortablePath, {pnpApi, target}); - await wrapper.writeManifest(); - - await wrapper.writeBinary(`bin/nodeServer.js` as PortablePath); + await wrapper.writeDefaults(); return wrapper; }; @@ -16,28 +14,15 @@ export const generateAstroLanguageServerBaseWrapper: GenerateBaseWrapper = async export const generateEslintBaseWrapper: GenerateBaseWrapper = async (pnpApi: PnpApi, target: PortablePath) => { const wrapper = new Wrapper(`eslint` as PortablePath, {pnpApi, target}); - await wrapper.writeManifest(); - - await wrapper.writeBinary(`bin/eslint.js` as PortablePath); - await wrapper.writeFile(`lib/api.js` as PortablePath, { - // Empty path to use the entrypoint and let Node.js resolve the correct path itself - requirePath: `` as PortablePath, - }); + await wrapper.writeDefaults(); return wrapper; }; export const generatePrettierBaseWrapper: GenerateBaseWrapper = async (pnpApi: PnpApi, target: PortablePath) => { - const wrapper = new Wrapper(`prettier` as PortablePath, {pnpApi, target}); + const wrapper = new Wrapper(`prettier` as PortablePath, {pnpApi, target, manifestOverrides: {exports: undefined}}); - await wrapper.writeManifest({ - main: `./index.js`, - }); - - await wrapper.writeBinary(`index.js` as PortablePath, { - // Empty path to use the entrypoint and let Node.js resolve the correct path itself - requirePath: `` as PortablePath, - }); + await wrapper.writeDefaults(); return wrapper; }; @@ -45,9 +30,7 @@ export const generatePrettierBaseWrapper: GenerateBaseWrapper = async (pnpApi: P export const generateRelayCompilerBaseWrapper: GenerateBaseWrapper = async (pnpApi: PnpApi, target: PortablePath) => { const wrapper = new Wrapper(`relay-compiler` as PortablePath, {pnpApi, target}); - await wrapper.writeManifest(); - - await wrapper.writeBinary(`cli.js` as PortablePath); + await wrapper.writeDefaults(); return wrapper; }; @@ -55,9 +38,7 @@ export const generateRelayCompilerBaseWrapper: GenerateBaseWrapper = async (pnpA export const generateTypescriptLanguageServerBaseWrapper: GenerateBaseWrapper = async (pnpApi: PnpApi, target: PortablePath) => { const wrapper = new Wrapper(`typescript-language-server` as PortablePath, {pnpApi, target}); - await wrapper.writeManifest(); - - await wrapper.writeBinary(`lib/cli.js` as PortablePath); + await wrapper.writeDefaults(); return wrapper; }; @@ -272,14 +253,10 @@ export const generateTypescriptBaseWrapper: GenerateBaseWrapper = async (pnpApi: const wrapper = new Wrapper(`typescript` as PortablePath, {pnpApi, target}); - await wrapper.writeManifest(); - - await wrapper.writeBinary(`bin/tsc` as PortablePath); - await wrapper.writeBinary(`bin/tsserver` as PortablePath); + await wrapper.writeDefaults(); await wrapper.writeFile(`lib/tsc.js` as PortablePath); await wrapper.writeFile(`lib/tsserver.js` as PortablePath, {wrapModule: tsServerMonkeyPatch}); - await wrapper.writeFile(`lib/typescript.js` as PortablePath); await wrapper.writeFile(`lib/tsserverlibrary.js` as PortablePath, {wrapModule: tsServerMonkeyPatch}); return wrapper; @@ -288,9 +265,7 @@ export const generateTypescriptBaseWrapper: GenerateBaseWrapper = async (pnpApi: export const generateSvelteLanguageServerBaseWrapper: GenerateBaseWrapper = async (pnpApi: PnpApi, target: PortablePath) => { const wrapper = new Wrapper(`svelte-language-server` as PortablePath, {pnpApi, target}); - await wrapper.writeManifest(); - - await wrapper.writeBinary(`bin/server.js` as PortablePath); + await wrapper.writeDefaults(); return wrapper; }; @@ -298,9 +273,7 @@ export const generateSvelteLanguageServerBaseWrapper: GenerateBaseWrapper = asyn export const generateFlowBinBaseWrapper: GenerateBaseWrapper = async (pnpApi: PnpApi, target: PortablePath) => { const wrapper = new Wrapper(`flow-bin` as PortablePath, {pnpApi, target}); - await wrapper.writeManifest(); - - await wrapper.writeBinary(`cli.js` as PortablePath); + await wrapper.writeDefaults(); return wrapper; }; diff --git a/packages/yarnpkg-sdks/sources/sdks/cocvim.ts b/packages/yarnpkg-sdks/sources/sdks/cocvim.ts index c22449497a6e..a07b6658df54 100644 --- a/packages/yarnpkg-sdks/sources/sdks/cocvim.ts +++ b/packages/yarnpkg-sdks/sources/sdks/cocvim.ts @@ -1,4 +1,4 @@ -import {PortablePath, npath, ppath} from '@yarnpkg/fslib'; +import {Filename, PortablePath, npath, ppath} from '@yarnpkg/fslib'; import {PnpApi} from '@yarnpkg/pnp'; import {Wrapper, GenerateIntegrationWrapper, IntegrationSdks} from '../generateSdk'; @@ -17,11 +17,9 @@ export const generateEslintWrapper: GenerateIntegrationWrapper = async (pnpApi: await addCocVimWorkspaceConfiguration(pnpApi, CocVimConfiguration.settings, { [`eslint.packageManager`]: `yarn`, [`eslint.nodePath`]: npath.fromPortablePath( - ppath.dirname(ppath.dirname(ppath.dirname( - wrapper.getProjectPathTo( - `lib/api.js` as PortablePath, - ), - ))), + ppath.dirname(ppath.dirname( + wrapper.getProjectPathTo(Filename.manifest), + )), ), }); }; diff --git a/packages/yarnpkg-sdks/sources/sdks/vscode.ts b/packages/yarnpkg-sdks/sources/sdks/vscode.ts index b478735509cc..6353ad92330f 100644 --- a/packages/yarnpkg-sdks/sources/sdks/vscode.ts +++ b/packages/yarnpkg-sdks/sources/sdks/vscode.ts @@ -1,4 +1,4 @@ -import {PortablePath, npath, ppath} from '@yarnpkg/fslib'; +import {Filename, PortablePath, npath, ppath} from '@yarnpkg/fslib'; import {PnpApi} from '@yarnpkg/pnp'; import {Wrapper, GenerateIntegrationWrapper, GenerateDefaultWrapper, IntegrationSdks} from '../generateSdk'; @@ -48,11 +48,9 @@ export const generateAstroLanguageServerWrapper: GenerateIntegrationWrapper = as export const generateEslintWrapper: GenerateIntegrationWrapper = async (pnpApi: PnpApi, target: PortablePath, wrapper: Wrapper) => { await addVSCodeWorkspaceConfiguration(pnpApi, VSCodeConfiguration.settings, { [`eslint.nodePath`]: npath.fromPortablePath( - ppath.dirname(ppath.dirname(ppath.dirname( - wrapper.getProjectPathTo( - `lib/api.js` as PortablePath, - ), - ))), + ppath.dirname(ppath.dirname( + wrapper.getProjectPathTo(Filename.manifest), + )), ), }); @@ -67,7 +65,7 @@ export const generatePrettierWrapper: GenerateIntegrationWrapper = async (pnpApi await addVSCodeWorkspaceConfiguration(pnpApi, VSCodeConfiguration.settings, { [`prettier.prettierPath`]: npath.fromPortablePath( wrapper.getProjectPathTo( - `index.js` as PortablePath, + ppath.normalize(wrapper.manifest.main), ), ), });