Skip to content

Commit

Permalink
[cli] pnpm support (#4540)
Browse files Browse the repository at this point in the history
* add js-yaml as dep

* able to scan pnpm workspace file

* should scan inner node_modules now

* fix tests

* create pnpm fixture

* add tett

* update js-yaml without stub
  • Loading branch information
Brianzchen committed Oct 23, 2023
1 parent a1ee858 commit b30ed37
Show file tree
Hide file tree
Showing 17 changed files with 336 additions and 28 deletions.
4 changes: 2 additions & 2 deletions cli/flow-typed/npm/jest_v27.x.x.js
@@ -1,5 +1,5 @@
// flow-typed signature: 5ddcf688200e3506308fdcfa78ca48d9
// flow-typed version: 644a595e77/jest_v27.x.x/flow_>=v0.134.x
// flow-typed signature: e79552dcbaf7786acff981b62cca0df0
// flow-typed version: 6912183195/jest_v27.x.x/flow_>=v0.134.x <=v0.200.x

type JestMockFn<TArguments: $ReadOnlyArray<*>, TReturn> = {
(...args: TArguments): TReturn,
Expand Down
152 changes: 152 additions & 0 deletions cli/flow-typed/npm/js-yaml_v4.x.x.js
@@ -0,0 +1,152 @@
// flow-typed signature: 653406de19061c8dabf31c65ca9e577c
// flow-typed version: 081baee5a4/js-yaml_v4.x.x/flow_>=v0.104.x

declare module 'js-yaml' {
declare type Mark = {|
buffer: string,
column: number,
line: number,
name: string,
position: number,
snippet: string,
|};

declare class YAMLException extends Error {
constructor(reason?: string, mark?: Mark): this;

toString(compact?: boolean): string;

name: string;

reason: string;

message: string;

mark: Mark;
}

declare type TypeConstructorOptions = {|
kind?: "sequence" | "scalar" | "mapping",
resolve?: (data: any) => boolean,
construct?: (data: any, type?: string) => any,
instanceOf?: { ... },
predicate?: (data: { ... }) => boolean,
represent?: ((data: { ... }) => any) | { [x: string]: (data: { ... }) => any },
representName?: (data: { ... }) => any,
defaultStyle?: string,
multi?: boolean,
styleAliases?: { [x: string]: any },
|};

declare class Type {
constructor(tag: string, opts?: TypeConstructorOptions): this;
kind: "sequence" | "scalar" | "mapping" | null;
resolve(data: any): boolean;
construct(data: any, type?: string): any;
instanceOf: { ... } | null;
predicate: ((data: { ... }) => boolean) | null;
represent: ((data: { ... }) => any) | { [x: string]: (data: { ... }) => any } | null;
representName: ((data: { ... }) => any) | null;
defaultStyle: string | null;
multi: boolean;
styleAliases: { [x: string]: any };
}

declare type State = {|
input: string,
filename: string | null,
schema: Schema,
onWarning: (this: null, e: YAMLException) => void,
json: boolean,
length: number,
position: number,
line: number,
lineStart: number,
lineIndent: number,
version: null | number,
checkLineBreaks: boolean,
kind: string,
result: any,
implicitTypes: Type[],
|};

declare type EventType = "open" | "close";

declare type LoadOptions = {|
/** string to be used as a file path in error/warning messages. */
filename?: string,
/** function to call on warning messages. */
onWarning?: (this: null, e: YAMLException) => void,
/** specifies a schema to use. */
schema?: Schema,
/** compatibility with JSON.parse behaviour. */
json?: boolean,
/** listener for parse events */
listener?: (this: State, eventType: EventType, state: State) => void,
|};

declare type LoadReturn = string | number | { [key: string]: any } | null | void;

declare type SchemaDefinition = {|
implicit?: Type[],
explicit?: Type[],
|};

declare class Schema {
constructor(definition: SchemaDefinition | Type[] | Type): this;
extend(types: SchemaDefinition | Type[] | Type): Schema;
}

declare type DumpOptions = {|
/** indentation width to use (in spaces). */
indent?: number,
/** when true, will not add an indentation level to array elements */
noArrayIndent?: boolean,
/** do not throw on invalid types (like function in the safe schema) and skip pairs and single values with such types. */
skipInvalid?: boolean,
/** specifies level of nesting, when to switch from block to flow style for collections. -1 means block style everwhere */
flowLevel?: number,
/** Each tag may have own set of styles. - "tag" => "style" map. */
styles?: { [x: string]: any },
/** specifies a schema to use. */
schema?: Schema,
/** if true, sort keys when dumping YAML. If a function, use the function to sort the keys. (default: false) */
sortKeys?: boolean | ((a: any, b: any) => number),
/** set max line width. (default: 80) */
lineWidth?: number,
/** if true, don't convert duplicate objects into references (default: false) */
noRefs?: boolean,
/** if true don't try to be compatible with older yaml versions. Currently: don't quote "yes", "no" and so on, as required for YAML 1.1 (default: false) */
noCompatMode?: boolean,
/**
* if true flow sequences will be condensed, omitting the space between `key: value` or `a, b`. Eg. `'[a,b]'` or `{a:{b:c}}`.
* Can be useful when using yaml for pretty URL query params as spaces are %-encoded. (default: false).
*/
condenseFlow?: boolean,
/** strings will be quoted using this quoting style. If you specify single quotes, double quotes will still be used for non-printable characters. (default: `'`) */
quotingType?: "'" | "\"",
/** if true, all non-key strings will be quoted even if they normally don't need to. (default: false) */
forceQuotes?: boolean,
/** callback `function (key, value)` called recursively on each key/value in source object (see `replacer` docs for `JSON.stringify`). */
replacer?: (key: string, value: any) => any,
|};

declare class Exports {
load(str: string, opts?: LoadOptions): LoadReturn;
loadAll(str: string, iterator?: null, opts?: LoadOptions): LoadReturn[];
loadAll(str: string, iterator: (doc: LoadReturn) => void, opts?: LoadOptions): void;
dump(obj: any, opts?: DumpOptions): string;
Schema: typeof Schema,
YAMLException: typeof YAMLException,
/** only strings, arrays and plain objects: http://www.yaml.org/spec/1.2/spec.html#id2802346 */
FAILSAFE_SCHEMA: Schema,
/** only strings, arrays and plain objects: http://www.yaml.org/spec/1.2/spec.html#id2802346 */
JSON_SCHEMA: Schema,
/** same as JSON_SCHEMA: http://www.yaml.org/spec/1.2/spec.html#id2804923 */
CORE_SCHEMA: Schema,
/** all supported YAML types */
DEFAULT_SCHEMA: Schema,
}

declare module.exports: Exports;
}
4 changes: 2 additions & 2 deletions cli/flow-typed/npm/lodash_v4.x.x.js
@@ -1,5 +1,5 @@
// flow-typed signature: fd88436625196dbb8bf813a1f1684e58
// flow-typed version: 179ae71d58/lodash_v4.x.x/flow_>=v0.155.x
// flow-typed signature: 306d9e867540bad092198f975ca96b18
// flow-typed version: 6912183195/lodash_v4.x.x/flow_>=v0.155.x <=v0.200.x

declare module "lodash" {
declare type Path = $ReadOnlyArray<string | number> | string | number;
Expand Down
4 changes: 2 additions & 2 deletions cli/flow-typed/npm/prettier_v1.x.x.js
@@ -1,5 +1,5 @@
// flow-typed signature: a18c145fa181510ba16b10d17fc3a5a8
// flow-typed version: 01acbe56d4/prettier_v1.x.x/flow_>=v0.104.x
// flow-typed signature: 45d6719922da287e98e2f6afd9168af4
// flow-typed version: 6912183195/prettier_v1.x.x/flow_>=v0.104.x <=v0.200.x

declare module "prettier" {
declare export type AST = { [key: string]: any, ... };
Expand Down
4 changes: 2 additions & 2 deletions cli/flow-typed/npm/yargs_v15.x.x.js
@@ -1,5 +1,5 @@
// flow-typed signature: 1f51ede354b708d1bf2ac3d98fd21d0b
// flow-typed version: fe275d55fd/yargs_v15.x.x/flow_>=v0.118.x
// flow-typed signature: 9aa4b5b65a89b6c5ddbb3f442e747bbb
// flow-typed version: 830640f51c/yargs_v15.x.x/flow_>=v0.118.x <=v0.200.x

declare module "yargs" {
declare type Argv = {
Expand Down
1 change: 1 addition & 0 deletions cli/package.json
Expand Up @@ -39,6 +39,7 @@
"fs-extra": "^8.1.0",
"glob": "^7.1.6",
"got": "^11.8.5",
"js-yaml": "^4.1.0",
"md5": "^2.2.1",
"mkdirp": "^1.0.3",
"node-stream-zip": "^1.15.0",
Expand Down
Empty file.
@@ -0,0 +1,7 @@
{
"name": "root-workspace",
"private": true,
"devDependencies": {
"flow-bin": "^0.43.0"
}
}
@@ -0,0 +1,8 @@
{
"name": "a",
"version": "0.0.0",
"peerDependencies": {
"c": "*",
"foo": "^1.0.0"
}
}
@@ -0,0 +1,11 @@
{
"name": "b",
"version": "0.0.0",
"dependencies": {
"a": "*"
},
"devDependencies": {
"c": "*",
"foo": "^1.0.0"
}
}
@@ -0,0 +1,7 @@
{
"name": "c",
"version": "0.0.0",
"dependencies": {
"untyped": "^1.0.0"
}
}
@@ -0,0 +1,2 @@
packages:
- 'packages/*'
70 changes: 70 additions & 0 deletions cli/src/commands/__tests__/install-test.js
Expand Up @@ -1811,6 +1811,76 @@ declare type jsx$HTMLElementProps = {||}`;
});
});

it('supports pnpm workspaces', () => {
return fakeProjectEnv(async FLOWPROJ_DIR => {
await copyDir(path.join(FIXTURE_ROOT, 'pnpm-workspace'), FLOWPROJ_DIR);

await mkdirp(
path.join(FLOWPROJ_DIR, 'packages', 'c', 'node_modules', 'untyped'),
);
await touchFile(
path.join(
FLOWPROJ_DIR,
'packages',
'c',
'node_modules',
'untyped',
'package.json',
),
);
await writePkgJson(
path.join(
FLOWPROJ_DIR,
'packages',
'c',
'node_modules',
'untyped',
'package.json',
),
{
name: 'untyped',
dependencies: {
random: '^0.5.0',
},
},
);
await touchFile(
path.join(
FLOWPROJ_DIR,
'packages',
'c',
'node_modules',
'untyped',
'index.js.flow',
),
);

// Run the install command
await run({
...defaultRunProps,
ignoreDeps: [],
});

// Installs libdefs
expect(
await fs.readdir(path.join(FLOWPROJ_DIR, 'flow-typed', 'npm')),
).toEqual([
'a_vx.x.x.js',
'c_vx.x.x.js',
'flow-bin_v0.x.x.js',
'foo_v1.x.x.js',
]);

// Signs installed libdefs
const fooLibDefContents = await fs.readFile(
path.join(FLOWPROJ_DIR, 'flow-typed', 'npm', 'foo_v1.x.x.js'),
'utf8',
);
expect(fooLibDefContents).toContain('// flow-typed signature: ');
expect(fooLibDefContents).toContain('// flow-typed version: ');
});
});

it('supports legacy workspace', () => {
return fakeProjectEnv(async FLOWPROJ_DIR => {
await copyDir(
Expand Down
15 changes: 12 additions & 3 deletions cli/src/commands/install.js
Expand Up @@ -386,6 +386,8 @@ async function installNpmLibDefs({
}
}

let workspacesPkgJsonData;

// If a specific pkg/version was specified, only add those packages.
// Otherwise, extract dependencies from the package.json
if (explicitLibDefs.length > 0) {
Expand All @@ -394,7 +396,8 @@ async function installNpmLibDefs({
const termMatches = term.match(/(@[^@\/]+\/)?([^@]+)@(.+)/);
if (termMatches == null) {
const pkgJsonData = await getPackageJsonData(cwd);
const workspacesPkgJsonData = await findWorkspacesPackages(
workspacesPkgJsonData = await findWorkspacesPackages(
cwd,
pkgJsonData,
ftConfig,
);
Expand Down Expand Up @@ -423,7 +426,8 @@ async function installNpmLibDefs({
console.log(`• Searching for ${libdefsToSearchFor.size} libdefs...`);
} else {
const pkgJsonData = await getPackageJsonData(cwd);
const workspacesPkgJsonData = await findWorkspacesPackages(
workspacesPkgJsonData = await findWorkspacesPackages(
cwd,
pkgJsonData,
ftConfig,
);
Expand Down Expand Up @@ -530,7 +534,12 @@ async function installNpmLibDefs({
const typedMissingLibDefs: Array<[string, string, string]> = [];
await Promise.all(
unavailableLibDefs.map(async ({name: pkgName, ver: pkgVer}) => {
const hasFlowFiles = await pkgHasFlowFiles(cwd, pkgName, pnpResolver);
const hasFlowFiles = await pkgHasFlowFiles(
cwd,
pkgName,
pnpResolver,
workspacesPkgJsonData,
);
if (hasFlowFiles.flowTyped && hasFlowFiles.path) {
typedMissingLibDefs.push([pkgName, pkgVer, hasFlowFiles.path]);
} else {
Expand Down
1 change: 1 addition & 0 deletions cli/src/lib/__tests__/libDefs-test.js
@@ -1,5 +1,6 @@
// @flow
jest.enableAutomock();
jest.unmock('js-yaml');
jest.unmock('../libDefs.js');
jest.unmock('../semver.js');
jest.unmock('semver');
Expand Down

0 comments on commit b30ed37

Please sign in to comment.