fix(mobile): resolve user avatars, chat alignment, timestamp formatting, and payslips API
This commit is contained in:
21
node_modules/@react-grab/cli/LICENSE
generated
vendored
Normal file
21
node_modules/@react-grab/cli/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Aiden Bai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
166
node_modules/@react-grab/cli/README.md
generated
vendored
Normal file
166
node_modules/@react-grab/cli/README.md
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
# @react-grab/cli
|
||||
|
||||
CLI for installing React Grab and configuring its activation behavior.
|
||||
|
||||
The CLI detects supported React projects, applies the dev-only setup, and can reconfigure an existing installation without hand-editing framework files.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
npx grab@latest init
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### `grab init`
|
||||
|
||||
Install React Grab in the current project. The CLI auto-detects the framework and applies the required development-only integration.
|
||||
|
||||
```bash
|
||||
npx grab@latest init
|
||||
```
|
||||
|
||||
| Option | Alias | Description |
|
||||
| ---------------- | ----- | ---------------------------------------- |
|
||||
| `--yes` | `-y` | Skip confirmation prompts |
|
||||
| `--force` | `-f` | Force overwrite existing config |
|
||||
| `--key <key>` | `-k` | Shortcut (e.g. Meta+K, Space) |
|
||||
| `--skip-install` | | Skip package installation |
|
||||
| `--pkg <pkg>` | | Custom package URL |
|
||||
| `--cwd <cwd>` | `-c` | Working directory (default: current dir) |
|
||||
|
||||
### `grab configure`
|
||||
|
||||
Update React Grab options. Runs an interactive wizard when called without flags.
|
||||
|
||||
```bash
|
||||
npx grab@latest configure
|
||||
```
|
||||
|
||||
| Option | Alias | Description |
|
||||
| ---------------------- | ----- | --------------------------------------------- |
|
||||
| `--yes` | `-y` | Skip confirmation prompts |
|
||||
| `--key <key>` | `-k` | Shortcut (e.g. Meta+K, Ctrl+Shift+G) |
|
||||
| `--mode <mode>` | `-m` | Activation mode (`toggle` or `hold`) |
|
||||
| `--hold-duration <ms>` | | Key hold duration in ms (hold mode, max 2000) |
|
||||
| `--allow-input <bool>` | | Allow activation inside input fields |
|
||||
| `--context-lines <n>` | | Max context lines (max 50) |
|
||||
| `--cdn <domain>` | | CDN domain (e.g. unpkg.com) |
|
||||
| `--cwd <cwd>` | `-c` | Working directory (default: current dir) |
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Interactive setup
|
||||
npx grab@latest init
|
||||
|
||||
# Non-interactive setup
|
||||
npx grab@latest init -y
|
||||
|
||||
# Set a custom shortcut
|
||||
npx grab@latest init -k "Meta+K"
|
||||
|
||||
# Change activation mode to hold
|
||||
npx grab@latest configure --mode hold --hold-duration 500
|
||||
|
||||
# Interactive configuration wizard
|
||||
npx grab@latest configure
|
||||
```
|
||||
|
||||
## Node API
|
||||
|
||||
`@react-grab/cli/api` exposes the same primitives that power the CLI, so you can build your own installer or wrap React Grab setup inside another tool. Importing it runs no code, unlike the CLI entry (`.`), which parses `argv` on import.
|
||||
|
||||
### `installReactGrab(options?)`
|
||||
|
||||
A high-level, non-interactive orchestrator. It detects the project, installs `react-grab` with the detected package manager, and applies the framework-specific development-only setup. It returns a structured result instead of printing or exiting.
|
||||
|
||||
```ts
|
||||
import { installReactGrab } from "@react-grab/cli/api";
|
||||
|
||||
const result = await installReactGrab({ cwd: process.cwd() });
|
||||
|
||||
console.log(result.framework); // "next" | "vite" | "tanstack" | "webpack"
|
||||
console.log(result.didInstallPackage); // whether react-grab was added to deps
|
||||
console.log(result.didChangeFile); // whether an entry file was modified
|
||||
console.log(result.transform.filePath); // the file that was (or would be) edited
|
||||
```
|
||||
|
||||
| Option | Type | Description |
|
||||
| ----------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| `cwd` | `string` | Project directory (default: `process.cwd()`) |
|
||||
| `framework` | `Framework` | Override framework detection |
|
||||
| `nextRouterType` | `NextRouterType` | Override Next.js router detection (`app` / `pages`) |
|
||||
| `packageManager` | `PackageManager` | Override package-manager detection |
|
||||
| `skipPackageInstall` | `boolean` | Skip installing the `react-grab` npm package |
|
||||
| `skipTransform` | `boolean` | Skip editing the framework entry file |
|
||||
| `dryRun` | `boolean` | Compute the changes without installing or writing |
|
||||
| `installPackageOptions` | `Omit<InstallPackageOptions, "cwd" \| "packageManager">` | Passed through to `installPackages` (e.g. `silent`, `isDev`); `cwd`/`packageManager` are controlled by the orchestrator |
|
||||
|
||||
Failures throw a `ReactGrabInstallError` whose `code` identifies the cause, with the original error preserved on `error.cause`:
|
||||
|
||||
- `unsupported-framework`: framework has no automatic setup (Remix, Astro, SvelteKit, Gatsby)
|
||||
- `unknown-framework`: no supported framework detected
|
||||
- `transform-failed`: entry file could not be located or edited
|
||||
- `install-failed`: package manager failed to install `react-grab`
|
||||
- `write-failed`: edited file could not be written
|
||||
|
||||
`installReactGrab` configures a single project at `cwd` and does not walk a monorepo. Point `cwd` at the app you want to set up, or call `findReactProjects` first to locate the apps in a workspace.
|
||||
|
||||
By default the call mutates your project: it runs the package manager and edits a framework entry file. Pass `dryRun: true` to compute the change set (returned on `result.transform`) without installing or writing.
|
||||
|
||||
### Low-level building blocks
|
||||
|
||||
If you want full control, compose the same functions the orchestrator uses:
|
||||
|
||||
```ts
|
||||
import {
|
||||
detectProject,
|
||||
previewTransform,
|
||||
applyTransform,
|
||||
installPackages,
|
||||
getPackagesToInstall,
|
||||
installSkill,
|
||||
} from "@react-grab/cli/api";
|
||||
|
||||
const project = await detectProject(process.cwd());
|
||||
const transform = previewTransform(
|
||||
project.projectRoot,
|
||||
project.framework,
|
||||
project.nextRouterType,
|
||||
project.isReactGrabConfigured,
|
||||
);
|
||||
```
|
||||
|
||||
Install the package only when it's missing, then write the previewed edit. `previewTransform` sets `noChanges` when React Grab is already wired up, so guard on it before calling `applyTransform`, which writes `transform.newContent` to `transform.filePath`:
|
||||
|
||||
```ts
|
||||
if (!project.hasReactGrab) {
|
||||
await installPackages(getPackagesToInstall(), {
|
||||
cwd: project.projectRoot,
|
||||
packageManager: project.packageManager,
|
||||
});
|
||||
}
|
||||
|
||||
if (transform.success && transform.newContent && !transform.noChanges) {
|
||||
applyTransform(transform);
|
||||
}
|
||||
|
||||
await installSkill({ cwd: project.projectRoot });
|
||||
```
|
||||
|
||||
The full export surface, each with its TypeScript types:
|
||||
|
||||
- Detection: `detectProject`, `detectFramework`, `detectPackageManager`, `detectNextRouterType`, `detectReactGrab`, `detectReactGrabConfigured`, `detectUnsupportedFramework`, `findReactProjects`
|
||||
- Transforms: `previewTransform`, `previewOptionsTransform`, `previewCdnTransform`, `applyTransform`, `hasFrameworkEntryPoint`
|
||||
- Installation: `installPackages`, `getPackagesToInstall`, `installSkill`, `removeSkill`
|
||||
|
||||
## Supported Frameworks
|
||||
|
||||
The CLI currently configures:
|
||||
|
||||
- Next.js App Router
|
||||
- Next.js Pages Router
|
||||
- Vite
|
||||
- TanStack Start
|
||||
- Webpack
|
||||
12
node_modules/@react-grab/cli/bin/cli.js
generated
vendored
Normal file
12
node_modules/@react-grab/cli/bin/cli.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
import module from "node:module";
|
||||
|
||||
if (module.enableCompileCache && !process.env.NODE_DISABLE_COMPILE_CACHE) {
|
||||
try {
|
||||
module.enableCompileCache();
|
||||
} catch {
|
||||
// Ignore compile-cache errors.
|
||||
}
|
||||
}
|
||||
|
||||
await import("../dist/cli.js");
|
||||
70
node_modules/@react-grab/cli/dist/api.cjs
generated
vendored
Normal file
70
node_modules/@react-grab/cli/dist/api.cjs
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env node
|
||||
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
||||
const require_install = require("./install-DlD3sLtm.cjs");
|
||||
let node_path = require("node:path");
|
||||
//#region src/utils/install-react-grab.ts
|
||||
var ReactGrabInstallError = class extends Error {
|
||||
code;
|
||||
constructor(message, code, options) {
|
||||
super(message, options);
|
||||
this.name = "ReactGrabInstallError";
|
||||
this.code = code;
|
||||
}
|
||||
};
|
||||
const installReactGrab = async (options = {}) => {
|
||||
const project = await require_install.detectProject((0, node_path.resolve)(options.cwd ?? process.cwd()));
|
||||
const framework = options.framework ?? project.framework;
|
||||
const packageManager = options.packageManager ?? project.packageManager;
|
||||
if (project.unsupportedFramework && !options.framework) throw new ReactGrabInstallError(`${project.unsupportedFramework} is not supported by automatic setup.`, "unsupported-framework");
|
||||
if (framework === "unknown") throw new ReactGrabInstallError("Could not detect a supported framework. Pass `framework` explicitly to override detection.", "unknown-framework");
|
||||
const nextRouterType = options.nextRouterType ?? (framework === "next" && project.nextRouterType === "unknown" ? require_install.detectNextRouterType(project.projectRoot) : project.nextRouterType);
|
||||
const alreadyConfigured = project.isReactGrabConfigured;
|
||||
const transform = require_install.previewTransform(project.projectRoot, framework, nextRouterType, alreadyConfigured);
|
||||
if (!transform.success && !options.skipTransform) throw new ReactGrabInstallError(transform.message, "transform-failed");
|
||||
const didInstallPackage = !options.skipPackageInstall && !options.dryRun && !project.hasReactGrab;
|
||||
if (didInstallPackage) try {
|
||||
await require_install.installPackages(require_install.getPackagesToInstall(), {
|
||||
...options.installPackageOptions,
|
||||
cwd: project.projectRoot,
|
||||
packageManager
|
||||
});
|
||||
} catch (error) {
|
||||
throw new ReactGrabInstallError(error instanceof Error ? error.message : "Failed to install the react-grab package.", "install-failed", { cause: error });
|
||||
}
|
||||
const didChangeFile = !transform.noChanges && Boolean(transform.newContent) && !options.skipTransform && !options.dryRun;
|
||||
if (didChangeFile) {
|
||||
const writeResult = require_install.applyTransform(transform);
|
||||
if (!writeResult.success) throw new ReactGrabInstallError(writeResult.error ?? `Failed to write to ${transform.filePath}`, "write-failed");
|
||||
}
|
||||
return {
|
||||
projectRoot: project.projectRoot,
|
||||
framework,
|
||||
nextRouterType,
|
||||
packageManager,
|
||||
alreadyConfigured,
|
||||
didInstallPackage,
|
||||
didChangeFile,
|
||||
dryRun: Boolean(options.dryRun),
|
||||
transform
|
||||
};
|
||||
};
|
||||
//#endregion
|
||||
exports.ReactGrabInstallError = ReactGrabInstallError;
|
||||
exports.applyTransform = require_install.applyTransform;
|
||||
exports.detectFramework = require_install.detectFramework;
|
||||
exports.detectNextRouterType = require_install.detectNextRouterType;
|
||||
exports.detectPackageManager = require_install.detectPackageManager;
|
||||
exports.detectProject = require_install.detectProject;
|
||||
exports.detectReactGrab = require_install.detectReactGrab;
|
||||
exports.detectReactGrabConfigured = require_install.detectReactGrabConfigured;
|
||||
exports.detectUnsupportedFramework = require_install.detectUnsupportedFramework;
|
||||
exports.findReactProjects = require_install.findReactProjects;
|
||||
exports.getPackagesToInstall = require_install.getPackagesToInstall;
|
||||
exports.hasFrameworkEntryPoint = require_install.hasFrameworkEntryPoint;
|
||||
exports.installPackages = require_install.installPackages;
|
||||
exports.installReactGrab = installReactGrab;
|
||||
exports.installSkill = require_install.installSkill;
|
||||
exports.previewCdnTransform = require_install.previewCdnTransform;
|
||||
exports.previewOptionsTransform = require_install.previewOptionsTransform;
|
||||
exports.previewTransform = require_install.previewTransform;
|
||||
exports.removeSkill = require_install.removeSkill;
|
||||
121
node_modules/@react-grab/cli/dist/api.d.cts
generated
vendored
Normal file
121
node_modules/@react-grab/cli/dist/api.d.cts
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env node
|
||||
import { SkillAgentType, add } from "agent-install/skill";
|
||||
|
||||
//#region src/utils/detect.d.ts
|
||||
type PackageManager = "npm" | "yarn" | "pnpm" | "bun";
|
||||
type Framework = "next" | "vite" | "tanstack" | "webpack" | "unknown";
|
||||
type NextRouterType = "app" | "pages" | "unknown";
|
||||
type UnsupportedFramework = "remix" | "astro" | "sveltekit" | "gatsby" | null;
|
||||
interface ProjectInfo {
|
||||
packageManager: PackageManager;
|
||||
framework: Framework;
|
||||
nextRouterType: NextRouterType;
|
||||
isMonorepo: boolean;
|
||||
projectRoot: string;
|
||||
hasReactGrab: boolean;
|
||||
isReactGrabConfigured: boolean;
|
||||
reactGrabVersion: string | null;
|
||||
unsupportedFramework: UnsupportedFramework;
|
||||
}
|
||||
declare const detectPackageManager: (projectRoot: string) => Promise<PackageManager>;
|
||||
declare const detectFramework: (projectRoot: string) => Framework;
|
||||
declare const detectNextRouterType: (projectRoot: string) => NextRouterType;
|
||||
interface WorkspaceProject {
|
||||
name: string;
|
||||
path: string;
|
||||
framework: Framework;
|
||||
}
|
||||
declare const findReactProjects: (projectRoot: string) => WorkspaceProject[];
|
||||
declare const detectReactGrabConfigured: (projectRoot: string) => boolean;
|
||||
declare const detectReactGrab: (projectRoot: string) => boolean;
|
||||
declare const detectUnsupportedFramework: (projectRoot: string) => UnsupportedFramework;
|
||||
declare const detectProject: (projectRoot?: string) => Promise<ProjectInfo>;
|
||||
//#endregion
|
||||
//#region src/utils/transform.d.ts
|
||||
interface TransformResult {
|
||||
success: boolean;
|
||||
filePath: string;
|
||||
message: string;
|
||||
originalContent?: string;
|
||||
newContent?: string;
|
||||
noChanges?: boolean;
|
||||
}
|
||||
interface ReactGrabOptions {
|
||||
activationKey?: string;
|
||||
activationMode?: "toggle" | "hold";
|
||||
keyHoldDuration?: number;
|
||||
allowActivationInsideInput?: boolean;
|
||||
maxContextLines?: number;
|
||||
}
|
||||
declare const hasFrameworkEntryPoint: (projectRoot: string, framework: Framework, nextRouterType: NextRouterType) => boolean;
|
||||
declare const previewTransform: (projectRoot: string, framework: Framework, nextRouterType: NextRouterType, reactGrabAlreadyConfigured?: boolean) => TransformResult;
|
||||
declare const applyTransform: (result: TransformResult) => {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
};
|
||||
declare const previewOptionsTransform: (projectRoot: string, framework: Framework, nextRouterType: NextRouterType, options: ReactGrabOptions) => TransformResult;
|
||||
declare const previewCdnTransform: (projectRoot: string, framework: Framework, nextRouterType: NextRouterType, targetCdnDomain: string) => TransformResult;
|
||||
//#endregion
|
||||
//#region src/utils/install.d.ts
|
||||
interface InstallPackageOptions {
|
||||
cwd?: string;
|
||||
isDev?: boolean;
|
||||
silent?: boolean;
|
||||
packageManager?: PackageManager;
|
||||
preferOffline?: boolean;
|
||||
additionalArgs?: string[];
|
||||
}
|
||||
declare const installPackages: (packages: string[], options?: InstallPackageOptions) => Promise<void>;
|
||||
declare const getPackagesToInstall: (includeReactGrab?: boolean) => string[];
|
||||
//#endregion
|
||||
//#region src/utils/install-react-grab.d.ts
|
||||
type ReactGrabInstallErrorCode = "unsupported-framework" | "unknown-framework" | "transform-failed" | "install-failed" | "write-failed";
|
||||
declare class ReactGrabInstallError extends Error {
|
||||
readonly code: ReactGrabInstallErrorCode;
|
||||
constructor(message: string, code: ReactGrabInstallErrorCode, options?: ErrorOptions);
|
||||
}
|
||||
interface InstallReactGrabOptions {
|
||||
cwd?: string;
|
||||
framework?: Framework;
|
||||
nextRouterType?: NextRouterType;
|
||||
packageManager?: PackageManager;
|
||||
skipPackageInstall?: boolean;
|
||||
skipTransform?: boolean;
|
||||
dryRun?: boolean;
|
||||
installPackageOptions?: Omit<InstallPackageOptions, "cwd" | "packageManager">;
|
||||
}
|
||||
interface InstallReactGrabResult {
|
||||
projectRoot: string;
|
||||
framework: Framework;
|
||||
nextRouterType: NextRouterType;
|
||||
packageManager: PackageManager;
|
||||
alreadyConfigured: boolean;
|
||||
didInstallPackage: boolean;
|
||||
didChangeFile: boolean;
|
||||
dryRun: boolean;
|
||||
transform: TransformResult;
|
||||
}
|
||||
declare const installReactGrab: (options?: InstallReactGrabOptions) => Promise<InstallReactGrabResult>;
|
||||
//#endregion
|
||||
//#region src/utils/install-skill.d.ts
|
||||
interface InstallSkillOptions {
|
||||
agents?: SkillAgentType[];
|
||||
global?: boolean;
|
||||
cwd?: string;
|
||||
}
|
||||
declare const installSkill: ({
|
||||
agents,
|
||||
global,
|
||||
cwd
|
||||
}?: InstallSkillOptions) => Promise<Awaited<ReturnType<typeof add>>>;
|
||||
interface RemoveSkillOptions {
|
||||
cwd?: string;
|
||||
global?: boolean;
|
||||
}
|
||||
declare const removeSkill: ({
|
||||
cwd,
|
||||
global
|
||||
}?: RemoveSkillOptions) => Promise<SkillAgentType[]>;
|
||||
//#endregion
|
||||
export { type Framework, type InstallPackageOptions, type InstallReactGrabOptions, type InstallReactGrabResult, type InstallSkillOptions, type NextRouterType, type PackageManager, type ProjectInfo, ReactGrabInstallError, type ReactGrabInstallErrorCode, type ReactGrabOptions, type TransformResult, type UnsupportedFramework, type WorkspaceProject, applyTransform, detectFramework, detectNextRouterType, detectPackageManager, detectProject, detectReactGrab, detectReactGrabConfigured, detectUnsupportedFramework, findReactProjects, getPackagesToInstall, hasFrameworkEntryPoint, installPackages, installReactGrab, installSkill, previewCdnTransform, previewOptionsTransform, previewTransform, removeSkill };
|
||||
//# sourceMappingURL=api.d.cts.map
|
||||
1
node_modules/@react-grab/cli/dist/api.d.cts.map
generated
vendored
Normal file
1
node_modules/@react-grab/cli/dist/api.d.cts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"api.d.cts","names":[],"sources":["../src/utils/detect.ts","../src/utils/transform.ts","../src/utils/install.ts","../src/utils/install-react-grab.ts","../src/utils/install-skill.ts"],"mappings":";;;;KAOY,cAAA;AAAA,KACA,SAAA;AAAA,KACA,cAAA;AAAA,KACA,oBAAA;AAAA,UAEK,WAAA;EACf,cAAA,EAAgB,cAAA;EAChB,SAAA,EAAW,SAAA;EACX,cAAA,EAAgB,cAAA;EAChB,UAAA;EACA,WAAA;EACA,YAAA;EACA,qBAAA;EACA,gBAAA;EACA,oBAAA,EAAsB,oBAAA;AAAA;AAAA,cAKX,oBAAA,GAA8B,WAAA,aAAsB,OAAA,CAAQ,cAAA;AAAA,cA4D5D,eAAA,GAAmB,WAAA,aAAsB,SAAA;AAAA,cAYzC,oBAAA,GAAwB,WAAA,aAAsB,cAAA;AAAA,UAyC1C,gBAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA,EAAW,SAAA;AAAA;AAAA,cAqLA,iBAAA,GAAqB,WAAA,aAAsB,gBAAA;AAAA,cAuD3C,yBAAA,GAA6B,WAAA;AAAA,cAI7B,eAAA,GAAmB,WAAA;AAAA,cAGnB,0BAAA,GAA8B,WAAA,aAAsB,oBAAA;AAAA,cAqBpD,aAAA,GAAuB,WAAA,cAAsC,OAAA,CAAQ,WAAA;;;UC7XjE,eAAA;EACf,OAAA;EACA,QAAA;EACA,OAAA;EACA,eAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,UAGe,gBAAA;EACf,aAAA;EACA,cAAA;EACA,eAAA;EACA,0BAAA;EACA,eAAA;AAAA;AAAA,cAgVW,sBAAA,GACX,WAAA,UACA,SAAA,EAAW,SAAA,EACX,cAAA,EAAgB,cAAA;AAAA,cAiBL,gBAAA,GACX,WAAA,UACA,SAAA,EAAW,SAAA,EACX,cAAA,EAAgB,cAAA,EAChB,0BAAA,eACC,eAAA;AAAA,cAmCU,cAAA,GAAkB,MAAA,EAAQ,eAAA;EAAoB,OAAA;EAAkB,KAAA;AAAA;AAAA,cA6OhE,uBAAA,GACX,WAAA,UACA,SAAA,EAAW,SAAA,EACX,cAAA,EAAgB,cAAA,EAChB,OAAA,EAAS,gBAAA,KACR,eAAA;AAAA,cA0CU,mBAAA,GACX,WAAA,UACA,SAAA,EAAW,SAAA,EACX,cAAA,EAAgB,cAAA,EAChB,eAAA,aACC,eAAA;;;UC/sBc,qBAAA;EACf,GAAA;EACA,KAAA;EACA,MAAA;EACA,cAAA,GAAiB,cAAA;EACjB,aAAA;EACA,cAAA;AAAA;AAAA,cAGW,eAAA,GACX,QAAA,YACA,OAAA,GAAS,qBAAA,KACR,OAAA;AAAA,cAsCU,oBAAA,GAAwB,gBAAA;;;KC5CzB,yBAAA;AAAA,cAOC,qBAAA,SAA8B,KAAA;EAAA,SAChC,IAAA,EAAM,yBAAA;cAEH,OAAA,UAAiB,IAAA,EAAM,yBAAA,EAA2B,OAAA,GAAU,YAAA;AAAA;AAAA,UAOzD,uBAAA;EACf,GAAA;EACA,SAAA,GAAY,SAAA;EACZ,cAAA,GAAiB,cAAA;EACjB,cAAA,GAAiB,cAAA;EACjB,kBAAA;EACA,aAAA;EACA,MAAA;EACA,qBAAA,GAAwB,IAAA,CAAK,qBAAA;AAAA;AAAA,UAGd,sBAAA;EACf,WAAA;EACA,SAAA,EAAW,SAAA;EACX,cAAA,EAAgB,cAAA;EAChB,cAAA,EAAgB,cAAA;EAChB,iBAAA;EACA,iBAAA;EACA,aAAA;EACA,MAAA;EACA,SAAA,EAAW,eAAA;AAAA;AAAA,cAGA,gBAAA,GACX,OAAA,GAAS,uBAAA,KACR,OAAA,CAAQ,sBAAA;;;UCtBM,mBAAA;EACf,MAAA,GAAS,cAAA;EACT,MAAA;EACA,GAAA;AAAA;AAAA,cAGW,YAAA;EAAsB,MAAA;EAAA,MAAA;EAAA;AAAA,IAIhC,mBAAA,KAA2B,OAAA,CAAQ,OAAA,CAAQ,UAAA,QAAkB,GAAA;AAAA,UAKtD,kBAAA;EACR,GAAA;EACA,MAAA;AAAA;AAAA,cAKW,WAAA;EAAqB,GAAA;EAAA;AAAA,IAG/B,kBAAA,KAA0B,OAAA,CAAQ,cAAA"}
|
||||
121
node_modules/@react-grab/cli/dist/api.d.ts
generated
vendored
Normal file
121
node_modules/@react-grab/cli/dist/api.d.ts
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env node
|
||||
import { SkillAgentType, add } from "agent-install/skill";
|
||||
|
||||
//#region src/utils/detect.d.ts
|
||||
type PackageManager = "npm" | "yarn" | "pnpm" | "bun";
|
||||
type Framework = "next" | "vite" | "tanstack" | "webpack" | "unknown";
|
||||
type NextRouterType = "app" | "pages" | "unknown";
|
||||
type UnsupportedFramework = "remix" | "astro" | "sveltekit" | "gatsby" | null;
|
||||
interface ProjectInfo {
|
||||
packageManager: PackageManager;
|
||||
framework: Framework;
|
||||
nextRouterType: NextRouterType;
|
||||
isMonorepo: boolean;
|
||||
projectRoot: string;
|
||||
hasReactGrab: boolean;
|
||||
isReactGrabConfigured: boolean;
|
||||
reactGrabVersion: string | null;
|
||||
unsupportedFramework: UnsupportedFramework;
|
||||
}
|
||||
declare const detectPackageManager: (projectRoot: string) => Promise<PackageManager>;
|
||||
declare const detectFramework: (projectRoot: string) => Framework;
|
||||
declare const detectNextRouterType: (projectRoot: string) => NextRouterType;
|
||||
interface WorkspaceProject {
|
||||
name: string;
|
||||
path: string;
|
||||
framework: Framework;
|
||||
}
|
||||
declare const findReactProjects: (projectRoot: string) => WorkspaceProject[];
|
||||
declare const detectReactGrabConfigured: (projectRoot: string) => boolean;
|
||||
declare const detectReactGrab: (projectRoot: string) => boolean;
|
||||
declare const detectUnsupportedFramework: (projectRoot: string) => UnsupportedFramework;
|
||||
declare const detectProject: (projectRoot?: string) => Promise<ProjectInfo>;
|
||||
//#endregion
|
||||
//#region src/utils/transform.d.ts
|
||||
interface TransformResult {
|
||||
success: boolean;
|
||||
filePath: string;
|
||||
message: string;
|
||||
originalContent?: string;
|
||||
newContent?: string;
|
||||
noChanges?: boolean;
|
||||
}
|
||||
interface ReactGrabOptions {
|
||||
activationKey?: string;
|
||||
activationMode?: "toggle" | "hold";
|
||||
keyHoldDuration?: number;
|
||||
allowActivationInsideInput?: boolean;
|
||||
maxContextLines?: number;
|
||||
}
|
||||
declare const hasFrameworkEntryPoint: (projectRoot: string, framework: Framework, nextRouterType: NextRouterType) => boolean;
|
||||
declare const previewTransform: (projectRoot: string, framework: Framework, nextRouterType: NextRouterType, reactGrabAlreadyConfigured?: boolean) => TransformResult;
|
||||
declare const applyTransform: (result: TransformResult) => {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
};
|
||||
declare const previewOptionsTransform: (projectRoot: string, framework: Framework, nextRouterType: NextRouterType, options: ReactGrabOptions) => TransformResult;
|
||||
declare const previewCdnTransform: (projectRoot: string, framework: Framework, nextRouterType: NextRouterType, targetCdnDomain: string) => TransformResult;
|
||||
//#endregion
|
||||
//#region src/utils/install.d.ts
|
||||
interface InstallPackageOptions {
|
||||
cwd?: string;
|
||||
isDev?: boolean;
|
||||
silent?: boolean;
|
||||
packageManager?: PackageManager;
|
||||
preferOffline?: boolean;
|
||||
additionalArgs?: string[];
|
||||
}
|
||||
declare const installPackages: (packages: string[], options?: InstallPackageOptions) => Promise<void>;
|
||||
declare const getPackagesToInstall: (includeReactGrab?: boolean) => string[];
|
||||
//#endregion
|
||||
//#region src/utils/install-react-grab.d.ts
|
||||
type ReactGrabInstallErrorCode = "unsupported-framework" | "unknown-framework" | "transform-failed" | "install-failed" | "write-failed";
|
||||
declare class ReactGrabInstallError extends Error {
|
||||
readonly code: ReactGrabInstallErrorCode;
|
||||
constructor(message: string, code: ReactGrabInstallErrorCode, options?: ErrorOptions);
|
||||
}
|
||||
interface InstallReactGrabOptions {
|
||||
cwd?: string;
|
||||
framework?: Framework;
|
||||
nextRouterType?: NextRouterType;
|
||||
packageManager?: PackageManager;
|
||||
skipPackageInstall?: boolean;
|
||||
skipTransform?: boolean;
|
||||
dryRun?: boolean;
|
||||
installPackageOptions?: Omit<InstallPackageOptions, "cwd" | "packageManager">;
|
||||
}
|
||||
interface InstallReactGrabResult {
|
||||
projectRoot: string;
|
||||
framework: Framework;
|
||||
nextRouterType: NextRouterType;
|
||||
packageManager: PackageManager;
|
||||
alreadyConfigured: boolean;
|
||||
didInstallPackage: boolean;
|
||||
didChangeFile: boolean;
|
||||
dryRun: boolean;
|
||||
transform: TransformResult;
|
||||
}
|
||||
declare const installReactGrab: (options?: InstallReactGrabOptions) => Promise<InstallReactGrabResult>;
|
||||
//#endregion
|
||||
//#region src/utils/install-skill.d.ts
|
||||
interface InstallSkillOptions {
|
||||
agents?: SkillAgentType[];
|
||||
global?: boolean;
|
||||
cwd?: string;
|
||||
}
|
||||
declare const installSkill: ({
|
||||
agents,
|
||||
global,
|
||||
cwd
|
||||
}?: InstallSkillOptions) => Promise<Awaited<ReturnType<typeof add>>>;
|
||||
interface RemoveSkillOptions {
|
||||
cwd?: string;
|
||||
global?: boolean;
|
||||
}
|
||||
declare const removeSkill: ({
|
||||
cwd,
|
||||
global
|
||||
}?: RemoveSkillOptions) => Promise<SkillAgentType[]>;
|
||||
//#endregion
|
||||
export { type Framework, type InstallPackageOptions, type InstallReactGrabOptions, type InstallReactGrabResult, type InstallSkillOptions, type NextRouterType, type PackageManager, type ProjectInfo, ReactGrabInstallError, type ReactGrabInstallErrorCode, type ReactGrabOptions, type TransformResult, type UnsupportedFramework, type WorkspaceProject, applyTransform, detectFramework, detectNextRouterType, detectPackageManager, detectProject, detectReactGrab, detectReactGrabConfigured, detectUnsupportedFramework, findReactProjects, getPackagesToInstall, hasFrameworkEntryPoint, installPackages, installReactGrab, installSkill, previewCdnTransform, previewOptionsTransform, previewTransform, removeSkill };
|
||||
//# sourceMappingURL=api.d.ts.map
|
||||
1
node_modules/@react-grab/cli/dist/api.d.ts.map
generated
vendored
Normal file
1
node_modules/@react-grab/cli/dist/api.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"api.d.ts","names":[],"sources":["../src/utils/detect.ts","../src/utils/transform.ts","../src/utils/install.ts","../src/utils/install-react-grab.ts","../src/utils/install-skill.ts"],"mappings":";;;;KAOY,cAAA;AAAA,KACA,SAAA;AAAA,KACA,cAAA;AAAA,KACA,oBAAA;AAAA,UAEK,WAAA;EACf,cAAA,EAAgB,cAAA;EAChB,SAAA,EAAW,SAAA;EACX,cAAA,EAAgB,cAAA;EAChB,UAAA;EACA,WAAA;EACA,YAAA;EACA,qBAAA;EACA,gBAAA;EACA,oBAAA,EAAsB,oBAAA;AAAA;AAAA,cAKX,oBAAA,GAA8B,WAAA,aAAsB,OAAA,CAAQ,cAAA;AAAA,cA4D5D,eAAA,GAAmB,WAAA,aAAsB,SAAA;AAAA,cAYzC,oBAAA,GAAwB,WAAA,aAAsB,cAAA;AAAA,UAyC1C,gBAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA,EAAW,SAAA;AAAA;AAAA,cAqLA,iBAAA,GAAqB,WAAA,aAAsB,gBAAA;AAAA,cAuD3C,yBAAA,GAA6B,WAAA;AAAA,cAI7B,eAAA,GAAmB,WAAA;AAAA,cAGnB,0BAAA,GAA8B,WAAA,aAAsB,oBAAA;AAAA,cAqBpD,aAAA,GAAuB,WAAA,cAAsC,OAAA,CAAQ,WAAA;;;UC7XjE,eAAA;EACf,OAAA;EACA,QAAA;EACA,OAAA;EACA,eAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,UAGe,gBAAA;EACf,aAAA;EACA,cAAA;EACA,eAAA;EACA,0BAAA;EACA,eAAA;AAAA;AAAA,cAgVW,sBAAA,GACX,WAAA,UACA,SAAA,EAAW,SAAA,EACX,cAAA,EAAgB,cAAA;AAAA,cAiBL,gBAAA,GACX,WAAA,UACA,SAAA,EAAW,SAAA,EACX,cAAA,EAAgB,cAAA,EAChB,0BAAA,eACC,eAAA;AAAA,cAmCU,cAAA,GAAkB,MAAA,EAAQ,eAAA;EAAoB,OAAA;EAAkB,KAAA;AAAA;AAAA,cA6OhE,uBAAA,GACX,WAAA,UACA,SAAA,EAAW,SAAA,EACX,cAAA,EAAgB,cAAA,EAChB,OAAA,EAAS,gBAAA,KACR,eAAA;AAAA,cA0CU,mBAAA,GACX,WAAA,UACA,SAAA,EAAW,SAAA,EACX,cAAA,EAAgB,cAAA,EAChB,eAAA,aACC,eAAA;;;UC/sBc,qBAAA;EACf,GAAA;EACA,KAAA;EACA,MAAA;EACA,cAAA,GAAiB,cAAA;EACjB,aAAA;EACA,cAAA;AAAA;AAAA,cAGW,eAAA,GACX,QAAA,YACA,OAAA,GAAS,qBAAA,KACR,OAAA;AAAA,cAsCU,oBAAA,GAAwB,gBAAA;;;KC5CzB,yBAAA;AAAA,cAOC,qBAAA,SAA8B,KAAA;EAAA,SAChC,IAAA,EAAM,yBAAA;cAEH,OAAA,UAAiB,IAAA,EAAM,yBAAA,EAA2B,OAAA,GAAU,YAAA;AAAA;AAAA,UAOzD,uBAAA;EACf,GAAA;EACA,SAAA,GAAY,SAAA;EACZ,cAAA,GAAiB,cAAA;EACjB,cAAA,GAAiB,cAAA;EACjB,kBAAA;EACA,aAAA;EACA,MAAA;EACA,qBAAA,GAAwB,IAAA,CAAK,qBAAA;AAAA;AAAA,UAGd,sBAAA;EACf,WAAA;EACA,SAAA,EAAW,SAAA;EACX,cAAA,EAAgB,cAAA;EAChB,cAAA,EAAgB,cAAA;EAChB,iBAAA;EACA,iBAAA;EACA,aAAA;EACA,MAAA;EACA,SAAA,EAAW,eAAA;AAAA;AAAA,cAGA,gBAAA,GACX,OAAA,GAAS,uBAAA,KACR,OAAA,CAAQ,sBAAA;;;UCtBM,mBAAA;EACf,MAAA,GAAS,cAAA;EACT,MAAA;EACA,GAAA;AAAA;AAAA,cAGW,YAAA;EAAsB,MAAA;EAAA,MAAA;EAAA;AAAA,IAIhC,mBAAA,KAA2B,OAAA,CAAQ,OAAA,CAAQ,UAAA,QAAkB,GAAA;AAAA,UAKtD,kBAAA;EACR,GAAA;EACA,MAAA;AAAA;AAAA,cAKW,WAAA;EAAqB,GAAA;EAAA;AAAA,IAG/B,kBAAA,KAA0B,OAAA,CAAQ,cAAA"}
|
||||
53
node_modules/@react-grab/cli/dist/api.js
generated
vendored
Normal file
53
node_modules/@react-grab/cli/dist/api.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env node
|
||||
import { _ as detectReactGrabConfigured, a as previewCdnTransform, f as detectFramework, g as detectReactGrab, h as detectProject, i as hasFrameworkEntryPoint, l as installSkill, m as detectPackageManager, n as installPackages, o as previewOptionsTransform, p as detectNextRouterType, r as applyTransform, s as previewTransform, t as getPackagesToInstall, u as removeSkill, v as detectUnsupportedFramework, y as findReactProjects } from "./install-Car5vKPk.js";
|
||||
import { resolve } from "node:path";
|
||||
//#region src/utils/install-react-grab.ts
|
||||
var ReactGrabInstallError = class extends Error {
|
||||
code;
|
||||
constructor(message, code, options) {
|
||||
super(message, options);
|
||||
this.name = "ReactGrabInstallError";
|
||||
this.code = code;
|
||||
}
|
||||
};
|
||||
const installReactGrab = async (options = {}) => {
|
||||
const project = await detectProject(resolve(options.cwd ?? process.cwd()));
|
||||
const framework = options.framework ?? project.framework;
|
||||
const packageManager = options.packageManager ?? project.packageManager;
|
||||
if (project.unsupportedFramework && !options.framework) throw new ReactGrabInstallError(`${project.unsupportedFramework} is not supported by automatic setup.`, "unsupported-framework");
|
||||
if (framework === "unknown") throw new ReactGrabInstallError("Could not detect a supported framework. Pass `framework` explicitly to override detection.", "unknown-framework");
|
||||
const nextRouterType = options.nextRouterType ?? (framework === "next" && project.nextRouterType === "unknown" ? detectNextRouterType(project.projectRoot) : project.nextRouterType);
|
||||
const alreadyConfigured = project.isReactGrabConfigured;
|
||||
const transform = previewTransform(project.projectRoot, framework, nextRouterType, alreadyConfigured);
|
||||
if (!transform.success && !options.skipTransform) throw new ReactGrabInstallError(transform.message, "transform-failed");
|
||||
const didInstallPackage = !options.skipPackageInstall && !options.dryRun && !project.hasReactGrab;
|
||||
if (didInstallPackage) try {
|
||||
await installPackages(getPackagesToInstall(), {
|
||||
...options.installPackageOptions,
|
||||
cwd: project.projectRoot,
|
||||
packageManager
|
||||
});
|
||||
} catch (error) {
|
||||
throw new ReactGrabInstallError(error instanceof Error ? error.message : "Failed to install the react-grab package.", "install-failed", { cause: error });
|
||||
}
|
||||
const didChangeFile = !transform.noChanges && Boolean(transform.newContent) && !options.skipTransform && !options.dryRun;
|
||||
if (didChangeFile) {
|
||||
const writeResult = applyTransform(transform);
|
||||
if (!writeResult.success) throw new ReactGrabInstallError(writeResult.error ?? `Failed to write to ${transform.filePath}`, "write-failed");
|
||||
}
|
||||
return {
|
||||
projectRoot: project.projectRoot,
|
||||
framework,
|
||||
nextRouterType,
|
||||
packageManager,
|
||||
alreadyConfigured,
|
||||
didInstallPackage,
|
||||
didChangeFile,
|
||||
dryRun: Boolean(options.dryRun),
|
||||
transform
|
||||
};
|
||||
};
|
||||
//#endregion
|
||||
export { ReactGrabInstallError, applyTransform, detectFramework, detectNextRouterType, detectPackageManager, detectProject, detectReactGrab, detectReactGrabConfigured, detectUnsupportedFramework, findReactProjects, getPackagesToInstall, hasFrameworkEntryPoint, installPackages, installReactGrab, installSkill, previewCdnTransform, previewOptionsTransform, previewTransform, removeSkill };
|
||||
|
||||
//# sourceMappingURL=api.js.map
|
||||
1
node_modules/@react-grab/cli/dist/api.js.map
generated
vendored
Normal file
1
node_modules/@react-grab/cli/dist/api.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1986
node_modules/@react-grab/cli/dist/cli.cjs
generated
vendored
Normal file
1986
node_modules/@react-grab/cli/dist/cli.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
node_modules/@react-grab/cli/dist/cli.d.cts
generated
vendored
Normal file
2
node_modules/@react-grab/cli/dist/cli.d.cts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
export { };
|
||||
2
node_modules/@react-grab/cli/dist/cli.d.ts
generated
vendored
Normal file
2
node_modules/@react-grab/cli/dist/cli.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
export { };
|
||||
1984
node_modules/@react-grab/cli/dist/cli.js
generated
vendored
Normal file
1984
node_modules/@react-grab/cli/dist/cli.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/@react-grab/cli/dist/cli.js.map
generated
vendored
Normal file
1
node_modules/@react-grab/cli/dist/cli.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
864
node_modules/@react-grab/cli/dist/install-Car5vKPk.js
generated
vendored
Normal file
864
node_modules/@react-grab/cli/dist/install-Car5vKPk.js
generated
vendored
Normal file
@@ -0,0 +1,864 @@
|
||||
#!/usr/bin/env node
|
||||
import { basename, delimiter, dirname, join, resolve } from "node:path";
|
||||
import { accessSync, constants, existsSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
||||
import { detect } from "package-manager-detector/detect";
|
||||
import ignore from "ignore";
|
||||
import { add, detectInstalledSkillAgents, getCanonicalSkillsDir, getSkillAgentConfig, getSkillAgentDir, getSkillAgentTypes, isUniversalSkillAgent } from "agent-install/skill";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { x } from "tinyexec";
|
||||
//#region src/utils/react-grab-code.ts
|
||||
const REACT_GRAB_SPECIFIER_PATTERN = String.raw`react-grab(?:\/[^"']+)?`;
|
||||
const stripComments = (content) => content.replace(/<!--[\s\S]*?-->/g, "").replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
|
||||
const stripTypeOnlyReactGrabImports = (content) => {
|
||||
return content.replace(new RegExp(String.raw`import\s+type\s+[^;]+from\s+["']${REACT_GRAB_SPECIFIER_PATTERN}["'];?`, "g"), "").replace(new RegExp(String.raw`import\s*\{\s*type\s+[^,}]+(?:\s*,\s*type\s+[^,}]+)*\s*,?\s*\}\s*from\s+["']${REACT_GRAB_SPECIFIER_PATTERN}["'];?`, "g"), "");
|
||||
};
|
||||
const hasReactGrabSetupCode = (content) => {
|
||||
const setupCandidateContent = stripTypeOnlyReactGrabImports(stripComments(content));
|
||||
return [
|
||||
new RegExp(String.raw`import\s*\(\s*["']${REACT_GRAB_SPECIFIER_PATTERN}["']\s*\)`),
|
||||
new RegExp(String.raw`import\s+(?!type\b)(?:[^"';]+from\s+)?["']${REACT_GRAB_SPECIFIER_PATTERN}["']`),
|
||||
new RegExp(String.raw`require\s*\(\s*["']${REACT_GRAB_SPECIFIER_PATTERN}["']\s*\)`),
|
||||
/<Script[\s\S]*?src\s*=\s*(?:["'][^"']*react-grab[^"']*["']|\{(?:["'][^"']*react-grab[^"']*["']|`[^`]*react-grab[^`]*`)\})/i,
|
||||
/<script[\s\S]*?src\s*=\s*["'][^"']*react-grab[^"']*["']/i
|
||||
].some((pattern) => pattern.test(setupCandidateContent));
|
||||
};
|
||||
//#endregion
|
||||
//#region src/utils/react-grab-setup-files.ts
|
||||
const COMPONENT_EXTENSIONS = [
|
||||
"tsx",
|
||||
"jsx",
|
||||
"ts",
|
||||
"js"
|
||||
];
|
||||
const INSTRUMENTATION_EXTENSIONS = [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"mts",
|
||||
"cts",
|
||||
"mjs",
|
||||
"cjs"
|
||||
];
|
||||
const ROUTE_EXTENSIONS = ["tsx", "jsx"];
|
||||
const createFileCandidates = (projectRoot, directories, baseName, extensions) => {
|
||||
const fileCandidates = [];
|
||||
for (const directory of directories) for (const extension of extensions) fileCandidates.push(join(projectRoot, directory, `${baseName}.${extension}`));
|
||||
return fileCandidates;
|
||||
};
|
||||
const findExistingFile = (fileCandidates) => {
|
||||
for (const filePath of fileCandidates) if (existsSync(filePath)) return filePath;
|
||||
return null;
|
||||
};
|
||||
const getLayoutFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["app", "src/app"], "layout", COMPONENT_EXTENSIONS);
|
||||
const getDocumentFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["pages", "src/pages"], "_document", COMPONENT_EXTENSIONS);
|
||||
const getInstrumentationFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["", "src"], "instrumentation-client", INSTRUMENTATION_EXTENSIONS);
|
||||
const getIndexHtmlCandidates = (projectRoot) => [join(projectRoot, "index.html"), join(projectRoot, "public", "index.html")];
|
||||
const getEntryFileCandidates = (projectRoot) => [...createFileCandidates(projectRoot, ["src"], "index", COMPONENT_EXTENSIONS), ...createFileCandidates(projectRoot, ["src"], "main", COMPONENT_EXTENSIONS)];
|
||||
const getTanStackRootFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["src/routes", "app/routes"], "__root", ROUTE_EXTENSIONS);
|
||||
const getReactGrabSetupFileCandidates = (projectRoot) => [
|
||||
...getLayoutFileCandidates(projectRoot),
|
||||
...getDocumentFileCandidates(projectRoot),
|
||||
...getInstrumentationFileCandidates(projectRoot),
|
||||
...getIndexHtmlCandidates(projectRoot),
|
||||
...getEntryFileCandidates(projectRoot),
|
||||
...getTanStackRootFileCandidates(projectRoot)
|
||||
];
|
||||
const findLayoutFile = (projectRoot) => findExistingFile(getLayoutFileCandidates(projectRoot));
|
||||
const findDocumentFile = (projectRoot) => findExistingFile(getDocumentFileCandidates(projectRoot));
|
||||
const findIndexHtml = (projectRoot) => findExistingFile(getIndexHtmlCandidates(projectRoot));
|
||||
const findEntryFile = (projectRoot) => findExistingFile(getEntryFileCandidates(projectRoot));
|
||||
const findTanStackRootFile = (projectRoot) => findExistingFile(getTanStackRootFileCandidates(projectRoot));
|
||||
const isInstrumentationFile = (filePath) => /(?:^|[/\\])instrumentation-client\.[cm]?[jt]sx?$/.test(filePath);
|
||||
//#endregion
|
||||
//#region src/utils/detect.ts
|
||||
const VALID_PACKAGE_MANAGERS = new Set([
|
||||
"npm",
|
||||
"yarn",
|
||||
"pnpm",
|
||||
"bun"
|
||||
]);
|
||||
const detectPackageManager = async (projectRoot) => {
|
||||
const result = await detect({ cwd: projectRoot });
|
||||
if (result?.agent) {
|
||||
const managerName = result.agent.split("@")[0];
|
||||
if (VALID_PACKAGE_MANAGERS.has(managerName)) return managerName;
|
||||
}
|
||||
return "npm";
|
||||
};
|
||||
const CONFIG_EXTENSIONS = [
|
||||
"ts",
|
||||
"mts",
|
||||
"cts",
|
||||
"js",
|
||||
"mjs",
|
||||
"cjs"
|
||||
];
|
||||
const hasConfigFile = (projectRoot, configBaseName) => CONFIG_EXTENSIONS.some((extension) => existsSync(join(projectRoot, `${configBaseName}.${extension}`)));
|
||||
const readMergedDependencies = (projectRoot) => {
|
||||
const packageJsonPath = join(projectRoot, "package.json");
|
||||
if (!existsSync(packageJsonPath)) return null;
|
||||
try {
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
||||
return {
|
||||
...packageJson.dependencies,
|
||||
...packageJson.devDependencies
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const detectFrameworkFromDependencies = (dependencies) => {
|
||||
if (!dependencies) return "unknown";
|
||||
if (dependencies["next"]) return "next";
|
||||
if (dependencies["@tanstack/react-start"]) return "tanstack";
|
||||
if (dependencies["vite"]) return "vite";
|
||||
if (dependencies["webpack"]) return "webpack";
|
||||
return "unknown";
|
||||
};
|
||||
const detectFrameworkFromConfigFiles = (projectRoot) => {
|
||||
if (hasConfigFile(projectRoot, "next.config")) return "next";
|
||||
if (hasConfigFile(projectRoot, "app.config")) return "tanstack";
|
||||
if (hasConfigFile(projectRoot, "vite.config")) return "vite";
|
||||
if (hasConfigFile(projectRoot, "webpack.config")) return "webpack";
|
||||
return "unknown";
|
||||
};
|
||||
const findEnclosingMonorepoRoot = (projectRoot) => {
|
||||
let currentDirectory = dirname(projectRoot);
|
||||
while (currentDirectory !== dirname(currentDirectory)) {
|
||||
if (detectMonorepo(currentDirectory)) return currentDirectory;
|
||||
currentDirectory = dirname(currentDirectory);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const detectFramework = (projectRoot) => {
|
||||
const localFramework = detectFrameworkFromDependencies(readMergedDependencies(projectRoot));
|
||||
if (localFramework !== "unknown") return localFramework;
|
||||
return detectFrameworkFromConfigFiles(projectRoot);
|
||||
};
|
||||
const detectFrameworkFromMonorepoRoot = (projectRoot) => {
|
||||
const monorepoRoot = findEnclosingMonorepoRoot(projectRoot);
|
||||
if (!monorepoRoot) return "unknown";
|
||||
return detectFrameworkFromDependencies(readMergedDependencies(monorepoRoot));
|
||||
};
|
||||
const detectNextRouterType = (projectRoot) => {
|
||||
const hasAppDir = existsSync(join(projectRoot, "app"));
|
||||
const hasSrcAppDir = existsSync(join(projectRoot, "src", "app"));
|
||||
const hasPagesDir = existsSync(join(projectRoot, "pages"));
|
||||
const hasSrcPagesDir = existsSync(join(projectRoot, "src", "pages"));
|
||||
if (hasAppDir || hasSrcAppDir) return "app";
|
||||
if (hasPagesDir || hasSrcPagesDir) return "pages";
|
||||
return "unknown";
|
||||
};
|
||||
const detectMonorepo = (projectRoot) => {
|
||||
if (existsSync(join(projectRoot, "pnpm-workspace.yaml"))) return true;
|
||||
if (existsSync(join(projectRoot, "lerna.json"))) return true;
|
||||
const packageJsonPath = join(projectRoot, "package.json");
|
||||
if (existsSync(packageJsonPath)) try {
|
||||
if (JSON.parse(readFileSync(packageJsonPath, "utf-8")).workspaces) return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const getWorkspacePatterns = (projectRoot) => {
|
||||
const patterns = [];
|
||||
const pnpmWorkspacePath = join(projectRoot, "pnpm-workspace.yaml");
|
||||
if (existsSync(pnpmWorkspacePath)) {
|
||||
const lines = readFileSync(pnpmWorkspacePath, "utf-8").split("\n");
|
||||
let inPackages = false;
|
||||
for (const line of lines) {
|
||||
if (line.match(/^packages:\s*$/)) {
|
||||
inPackages = true;
|
||||
continue;
|
||||
}
|
||||
if (inPackages) {
|
||||
if (line.match(/^[a-zA-Z]/) || line.trim() === "") {
|
||||
if (line.match(/^[a-zA-Z]/)) inPackages = false;
|
||||
continue;
|
||||
}
|
||||
const match = line.match(/^\s*-\s*['"]?([^'"#\n]+?)['"]?\s*$/);
|
||||
if (match) patterns.push(match[1].trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
const lernaJsonPath = join(projectRoot, "lerna.json");
|
||||
if (existsSync(lernaJsonPath)) try {
|
||||
const lernaJson = JSON.parse(readFileSync(lernaJsonPath, "utf-8"));
|
||||
if (Array.isArray(lernaJson.packages)) patterns.push(...lernaJson.packages);
|
||||
} catch {}
|
||||
const packageJsonPath = join(projectRoot, "package.json");
|
||||
if (existsSync(packageJsonPath)) try {
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
||||
if (Array.isArray(packageJson.workspaces)) patterns.push(...packageJson.workspaces);
|
||||
else if (packageJson.workspaces?.packages) patterns.push(...packageJson.workspaces.packages);
|
||||
} catch {}
|
||||
return [...new Set(patterns)];
|
||||
};
|
||||
const expandWorkspacePattern = (projectRoot, pattern) => {
|
||||
const isGlob = pattern.endsWith("/*");
|
||||
const basePath = join(projectRoot, pattern.replace(/\/\*$/, ""));
|
||||
if (!existsSync(basePath)) return [];
|
||||
if (!isGlob) return existsSync(join(basePath, "package.json")) ? [basePath] : [];
|
||||
const results = [];
|
||||
try {
|
||||
const entries = readdirSync(basePath, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
if (existsSync(join(basePath, entry.name, "package.json"))) results.push(join(basePath, entry.name));
|
||||
}
|
||||
} catch {
|
||||
return results;
|
||||
}
|
||||
return results;
|
||||
};
|
||||
const hasReactDependency = (projectPath) => {
|
||||
const dependencies = readMergedDependencies(projectPath);
|
||||
if (!dependencies) return false;
|
||||
return Boolean(dependencies["react"] || dependencies["react-dom"]);
|
||||
};
|
||||
const buildReactProject = (projectPath) => {
|
||||
const framework = detectFramework(projectPath);
|
||||
if (!hasReactDependency(projectPath) && framework === "unknown") return null;
|
||||
let name = basename(projectPath);
|
||||
const packageJsonPath = join(projectPath, "package.json");
|
||||
try {
|
||||
name = JSON.parse(readFileSync(packageJsonPath, "utf-8")).name || name;
|
||||
} catch {}
|
||||
return {
|
||||
name,
|
||||
path: projectPath,
|
||||
framework
|
||||
};
|
||||
};
|
||||
const findWorkspaceProjects = (projectRoot) => {
|
||||
const patterns = getWorkspacePatterns(projectRoot);
|
||||
const projects = [];
|
||||
for (const pattern of patterns) for (const projectPath of expandWorkspacePattern(projectRoot, pattern)) {
|
||||
const project = buildReactProject(projectPath);
|
||||
if (project) projects.push(project);
|
||||
}
|
||||
return projects;
|
||||
};
|
||||
const ALWAYS_IGNORED_DIRECTORIES = [
|
||||
"node_modules",
|
||||
".git",
|
||||
".next",
|
||||
".cache",
|
||||
".turbo",
|
||||
"dist",
|
||||
"build",
|
||||
"coverage",
|
||||
"test-results"
|
||||
];
|
||||
const loadGitignore = (projectRoot) => {
|
||||
const ignorer = ignore().add(ALWAYS_IGNORED_DIRECTORIES);
|
||||
const gitignorePath = join(projectRoot, ".gitignore");
|
||||
if (existsSync(gitignorePath)) try {
|
||||
ignorer.add(readFileSync(gitignorePath, "utf-8"));
|
||||
} catch {}
|
||||
return ignorer;
|
||||
};
|
||||
const scanDirectoryForProjects = (rootDirectory, ignorer, maxDepth, currentDepth = 0) => {
|
||||
if (currentDepth >= maxDepth) return [];
|
||||
if (!existsSync(rootDirectory)) return [];
|
||||
const projects = [];
|
||||
try {
|
||||
const entries = readdirSync(rootDirectory, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
if (ignorer.ignores(entry.name)) continue;
|
||||
const entryPath = join(rootDirectory, entry.name);
|
||||
if (existsSync(join(entryPath, "package.json"))) {
|
||||
const project = buildReactProject(entryPath);
|
||||
if (project) {
|
||||
projects.push(project);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
projects.push(...scanDirectoryForProjects(entryPath, ignorer, maxDepth, currentDepth + 1));
|
||||
}
|
||||
} catch {
|
||||
return projects;
|
||||
}
|
||||
return projects;
|
||||
};
|
||||
const MAX_SCAN_DEPTH = 2;
|
||||
const normalizePathForComparison = (filePath) => filePath.replace(/\\/g, "/");
|
||||
const findReactProjects = (projectRoot) => {
|
||||
const monorepoRoot = detectMonorepo(projectRoot) ? projectRoot : findEnclosingMonorepoRoot(projectRoot);
|
||||
if (monorepoRoot) {
|
||||
const workspaceProjects = findWorkspaceProjects(monorepoRoot);
|
||||
const localProject = projectRoot === monorepoRoot ? null : buildReactProject(projectRoot);
|
||||
const projects = localProject ? [localProject, ...workspaceProjects.filter((project) => normalizePathForComparison(project.path) !== normalizePathForComparison(localProject.path))] : workspaceProjects;
|
||||
if (projects.length > 0) return projects;
|
||||
}
|
||||
const scannedProjects = scanDirectoryForProjects(projectRoot, loadGitignore(projectRoot), MAX_SCAN_DEPTH);
|
||||
if (scannedProjects.length > 0) return scannedProjects;
|
||||
let currentDirectory = dirname(projectRoot);
|
||||
while (currentDirectory !== dirname(currentDirectory)) {
|
||||
const parentProject = buildReactProject(currentDirectory);
|
||||
if (parentProject) return [parentProject];
|
||||
currentDirectory = dirname(currentDirectory);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
const hasReactGrabSetupInFile = (filePath) => {
|
||||
if (!existsSync(filePath)) return false;
|
||||
try {
|
||||
return hasReactGrabSetupCode(readFileSync(filePath, "utf-8"));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const detectReactGrabDependency = (projectRoot) => {
|
||||
const dependencies = readMergedDependencies(projectRoot);
|
||||
return Boolean(dependencies?.["react-grab"]);
|
||||
};
|
||||
const detectReactGrabConfigured = (projectRoot) => {
|
||||
return getReactGrabSetupFileCandidates(projectRoot).some(hasReactGrabSetupInFile);
|
||||
};
|
||||
const detectReactGrab = (projectRoot) => detectReactGrabDependency(projectRoot) || detectReactGrabConfigured(projectRoot);
|
||||
const detectUnsupportedFramework = (projectRoot) => {
|
||||
const dependencies = readMergedDependencies(projectRoot);
|
||||
if (!dependencies) return null;
|
||||
if (dependencies["@remix-run/react"] || dependencies["remix"]) return "remix";
|
||||
if (dependencies["astro"]) return "astro";
|
||||
if (dependencies["@sveltejs/kit"]) return "sveltekit";
|
||||
if (dependencies["gatsby"]) return "gatsby";
|
||||
return null;
|
||||
};
|
||||
const detectReactGrabVersion = (projectRoot) => {
|
||||
const installedPackageJsonPath = join(projectRoot, "node_modules", "react-grab", "package.json");
|
||||
if (existsSync(installedPackageJsonPath)) try {
|
||||
return JSON.parse(readFileSync(installedPackageJsonPath, "utf-8")).version ?? null;
|
||||
} catch {}
|
||||
return null;
|
||||
};
|
||||
const detectProject = async (projectRoot = process.cwd()) => {
|
||||
const localFramework = detectFramework(projectRoot);
|
||||
const framework = localFramework === "unknown" ? detectFrameworkFromMonorepoRoot(projectRoot) : localFramework;
|
||||
const packageManager = await detectPackageManager(projectRoot);
|
||||
const isMonorepo = detectMonorepo(projectRoot) || findEnclosingMonorepoRoot(projectRoot) !== null;
|
||||
const isReactGrabConfigured = detectReactGrabConfigured(projectRoot);
|
||||
return {
|
||||
packageManager,
|
||||
framework,
|
||||
nextRouterType: framework === "next" ? detectNextRouterType(projectRoot) : "unknown",
|
||||
isMonorepo,
|
||||
projectRoot,
|
||||
hasReactGrab: detectReactGrabDependency(projectRoot) || isReactGrabConfigured,
|
||||
isReactGrabConfigured,
|
||||
reactGrabVersion: detectReactGrabVersion(projectRoot),
|
||||
unsupportedFramework: detectUnsupportedFramework(projectRoot)
|
||||
};
|
||||
};
|
||||
//#endregion
|
||||
//#region src/utils/detect-agents.ts
|
||||
const PATH_BINARIES = {
|
||||
"claude-code": ["claude"],
|
||||
codex: ["codex"],
|
||||
cursor: ["cursor", "cursor-agent"],
|
||||
droid: ["droid"],
|
||||
"gemini-cli": ["gemini"],
|
||||
"github-copilot": ["copilot"],
|
||||
opencode: ["opencode"],
|
||||
pi: ["pi", "omegon"]
|
||||
};
|
||||
const isCommandAvailable = (command) => {
|
||||
const pathDirectories = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
||||
for (const directory of pathDirectories) {
|
||||
const binaryPath = join(directory, command);
|
||||
try {
|
||||
if (statSync(binaryPath).isFile()) {
|
||||
accessSync(binaryPath, constants.X_OK);
|
||||
return true;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const detectAvailableAgents = async () => {
|
||||
const installedAgents = new Set(await detectInstalledSkillAgents());
|
||||
return getSkillAgentTypes().filter((agent) => {
|
||||
if (agent === "universal") return false;
|
||||
if (installedAgents.has(agent)) return true;
|
||||
return PATH_BINARIES[agent]?.some(isCommandAvailable) ?? false;
|
||||
});
|
||||
};
|
||||
//#endregion
|
||||
//#region src/utils/install-skill.ts
|
||||
const SKILL_NAME = "react-grab";
|
||||
const SKILL_SOURCE = fileURLToPath(new URL("../skills/react-grab", import.meta.url));
|
||||
const agentLabel = (agent) => getSkillAgentConfig(agent).displayName;
|
||||
const installedSkillDir = (agent, global, cwd) => join(isUniversalSkillAgent(agent) ? getCanonicalSkillsDir(global, cwd) : getSkillAgentDir(agent, {
|
||||
global,
|
||||
cwd
|
||||
}), SKILL_NAME);
|
||||
const installSkill = async ({ agents, global = false, cwd = process.cwd() } = {}) => {
|
||||
return add({
|
||||
source: SKILL_SOURCE,
|
||||
agents: agents ?? await detectAvailableAgents(),
|
||||
global,
|
||||
cwd,
|
||||
mode: "copy"
|
||||
});
|
||||
};
|
||||
const removeSkill = async ({ cwd = process.cwd(), global = false } = {}) => {
|
||||
const agents = await detectAvailableAgents();
|
||||
const removedAgents = [];
|
||||
const dirsToRemove = /* @__PURE__ */ new Set();
|
||||
for (const agent of agents) {
|
||||
const skillDir = installedSkillDir(agent, global, cwd);
|
||||
if (!existsSync(skillDir)) continue;
|
||||
removedAgents.push(agent);
|
||||
dirsToRemove.add(skillDir);
|
||||
}
|
||||
for (const skillDir of dirsToRemove) rmSync(skillDir, {
|
||||
recursive: true,
|
||||
force: true
|
||||
});
|
||||
return removedAgents;
|
||||
};
|
||||
//#endregion
|
||||
//#region src/utils/templates.ts
|
||||
const NEXT_APP_ROUTER_SCRIPT = `{process.env.NODE_ENV === "development" && (
|
||||
<Script
|
||||
src="//unpkg.com/react-grab/dist/index.global.js"
|
||||
crossOrigin="anonymous"
|
||||
strategy="beforeInteractive"
|
||||
/>
|
||||
)}`;
|
||||
const VITE_IMPORT = `if (import.meta.env.DEV) {
|
||||
import("react-grab");
|
||||
}`;
|
||||
const WEBPACK_IMPORT = `if (process.env.NODE_ENV === "development") {
|
||||
import("react-grab");
|
||||
}`;
|
||||
const TANSTACK_EFFECT = `useEffect(() => {
|
||||
if (import.meta.env.DEV) {
|
||||
void import("react-grab");
|
||||
}
|
||||
}, []);`;
|
||||
const SCRIPT_IMPORT = "import Script from \"next/script\";";
|
||||
//#endregion
|
||||
//#region src/utils/transform.ts
|
||||
const hasReactGrabInInstrumentation = (projectRoot) => {
|
||||
return findFileWithReactGrabSetup(getInstrumentationFileCandidates(projectRoot)) !== null;
|
||||
};
|
||||
const findFileWithReactGrabSetup = (fileCandidates) => {
|
||||
for (const filePath of fileCandidates) {
|
||||
if (!existsSync(filePath)) continue;
|
||||
if (hasReactGrabSetupCode(readFileSync(filePath, "utf-8"))) return filePath;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const alreadyConfiguredResult = (filePath) => ({
|
||||
success: true,
|
||||
filePath,
|
||||
message: "React Grab is already configured",
|
||||
noChanges: true
|
||||
});
|
||||
const transformNextAppRouter = (projectRoot, reactGrabAlreadyConfigured) => {
|
||||
const layoutPath = findLayoutFile(projectRoot);
|
||||
if (!layoutPath) return {
|
||||
success: false,
|
||||
filePath: "",
|
||||
message: "Could not find app/layout.tsx, app/layout.jsx, app/layout.ts, or app/layout.js"
|
||||
};
|
||||
const originalContent = readFileSync(layoutPath, "utf-8");
|
||||
let newContent = originalContent;
|
||||
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
||||
const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
|
||||
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(layoutPath);
|
||||
if (hasReactGrabInFile || hasReactGrabInInstrumentationFile) return {
|
||||
success: true,
|
||||
filePath: layoutPath,
|
||||
message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
|
||||
noChanges: true
|
||||
};
|
||||
if (!newContent.includes("import Script from \"next/script\"")) {
|
||||
const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
||||
if (importMatch) newContent = newContent.replace(importMatch[0], `${importMatch[0]}\n${SCRIPT_IMPORT}`);
|
||||
else newContent = `${SCRIPT_IMPORT}\n\n${newContent}`;
|
||||
}
|
||||
const headMatch = newContent.match(/<head[^>]*>/);
|
||||
if (headMatch) newContent = newContent.replace(headMatch[0], `${headMatch[0]}\n ${NEXT_APP_ROUTER_SCRIPT}`);
|
||||
else {
|
||||
const htmlMatch = newContent.match(/<html[^>]*>/);
|
||||
if (htmlMatch) newContent = newContent.replace(htmlMatch[0], `${htmlMatch[0]}\n <head>\n ${NEXT_APP_ROUTER_SCRIPT}\n </head>`);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
filePath: layoutPath,
|
||||
message: "Add React Grab",
|
||||
originalContent,
|
||||
newContent
|
||||
};
|
||||
};
|
||||
const transformNextPagesRouter = (projectRoot, reactGrabAlreadyConfigured) => {
|
||||
const documentPath = findDocumentFile(projectRoot);
|
||||
if (!documentPath) return {
|
||||
success: false,
|
||||
filePath: "",
|
||||
message: "Could not find pages/_document.tsx, pages/_document.jsx, pages/_document.ts, or pages/_document.js.\n\nTo set up React Grab with Pages Router, create pages/_document.tsx with:\n\n import { Html, Head, Main, NextScript } from \"next/document\";\n import Script from \"next/script\";\n\n export default function Document() {\n return (\n <Html>\n <Head>\n {process.env.NODE_ENV === \"development\" && (\n <Script src=\"//unpkg.com/react-grab/dist/index.global.js\" strategy=\"beforeInteractive\" />\n )}\n </Head>\n <body>\n <Main />\n <NextScript />\n </body>\n </Html>\n );\n }"
|
||||
};
|
||||
const originalContent = readFileSync(documentPath, "utf-8");
|
||||
let newContent = originalContent;
|
||||
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
||||
const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
|
||||
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(documentPath);
|
||||
if (hasReactGrabInFile || hasReactGrabInInstrumentationFile) return {
|
||||
success: true,
|
||||
filePath: documentPath,
|
||||
message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
|
||||
noChanges: true
|
||||
};
|
||||
if (!newContent.includes("import Script from \"next/script\"")) {
|
||||
const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
||||
if (importMatch) newContent = newContent.replace(importMatch[0], `${importMatch[0]}\n${SCRIPT_IMPORT}`);
|
||||
}
|
||||
const headMatch = newContent.match(/<Head[^>]*>/);
|
||||
if (headMatch) newContent = newContent.replace(headMatch[0], `${headMatch[0]}\n ${NEXT_APP_ROUTER_SCRIPT}`);
|
||||
return {
|
||||
success: true,
|
||||
filePath: documentPath,
|
||||
message: "Add React Grab",
|
||||
originalContent,
|
||||
newContent
|
||||
};
|
||||
};
|
||||
const checkExistingInstallation = (filePath, reactGrabAlreadyConfigured) => {
|
||||
if (!hasReactGrabSetupCode(readFileSync(filePath, "utf-8"))) return null;
|
||||
return {
|
||||
success: true,
|
||||
filePath,
|
||||
message: reactGrabAlreadyConfigured ? "React Grab is already configured" : "React Grab is already installed in this file",
|
||||
noChanges: true
|
||||
};
|
||||
};
|
||||
const transformVite = (projectRoot, reactGrabAlreadyConfigured) => {
|
||||
const entryPath = findEntryFile(projectRoot);
|
||||
const indexPath = findIndexHtml(projectRoot);
|
||||
if (indexPath) {
|
||||
const existingResult = checkExistingInstallation(indexPath, reactGrabAlreadyConfigured);
|
||||
if (existingResult) return existingResult;
|
||||
}
|
||||
if (!entryPath) return {
|
||||
success: false,
|
||||
filePath: "",
|
||||
message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
|
||||
};
|
||||
const existingResult = checkExistingInstallation(entryPath, reactGrabAlreadyConfigured);
|
||||
if (existingResult) return existingResult;
|
||||
const originalContent = readFileSync(entryPath, "utf-8");
|
||||
return {
|
||||
success: true,
|
||||
filePath: entryPath,
|
||||
message: "Add React Grab",
|
||||
originalContent,
|
||||
newContent: `${VITE_IMPORT}\n\n${originalContent}`
|
||||
};
|
||||
};
|
||||
const transformWebpack = (projectRoot, reactGrabAlreadyConfigured) => {
|
||||
const entryPath = findEntryFile(projectRoot);
|
||||
if (!entryPath) return {
|
||||
success: false,
|
||||
filePath: "",
|
||||
message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
|
||||
};
|
||||
const existingResult = checkExistingInstallation(entryPath, reactGrabAlreadyConfigured);
|
||||
if (existingResult) return existingResult;
|
||||
const originalContent = readFileSync(entryPath, "utf-8");
|
||||
return {
|
||||
success: true,
|
||||
filePath: entryPath,
|
||||
message: "Add React Grab",
|
||||
originalContent,
|
||||
newContent: `${WEBPACK_IMPORT}\n\n${originalContent}`
|
||||
};
|
||||
};
|
||||
const transformTanStack = (projectRoot, reactGrabAlreadyConfigured) => {
|
||||
const rootPath = findTanStackRootFile(projectRoot);
|
||||
if (!rootPath) return {
|
||||
success: false,
|
||||
filePath: "",
|
||||
message: "Could not find src/routes/__root.tsx or app/routes/__root.tsx.\n\nTo set up React Grab with TanStack Start, add this to your root route component:\n\n import { useEffect } from \"react\";\n\n useEffect(() => {\n if (import.meta.env.DEV) {\n void import(\"react-grab\");\n }\n }, []);"
|
||||
};
|
||||
const originalContent = readFileSync(rootPath, "utf-8");
|
||||
let newContent = originalContent;
|
||||
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
||||
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(rootPath);
|
||||
if (hasReactGrabInFile) return {
|
||||
success: true,
|
||||
filePath: rootPath,
|
||||
message: "React Grab is already installed in this file",
|
||||
noChanges: true
|
||||
};
|
||||
if (!/import\s+\{[^}]*useEffect[^}]*\}\s+from\s+["']react["']/.test(newContent)) {
|
||||
const reactImportMatch = newContent.match(/import\s+\{([^}]*)\}\s+from\s+["']react["'];?/);
|
||||
if (reactImportMatch) {
|
||||
const existingImports = reactImportMatch[1];
|
||||
newContent = newContent.replace(reactImportMatch[0], `import { ${existingImports.trim()}, useEffect } from "react";`);
|
||||
} else {
|
||||
const firstImportMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
||||
if (firstImportMatch) newContent = newContent.replace(firstImportMatch[0], `import { useEffect } from "react";\n${firstImportMatch[0]}`);
|
||||
else newContent = `import { useEffect } from "react";\n\n${newContent}`;
|
||||
}
|
||||
}
|
||||
const componentMatch = newContent.match(/function\s+(\w+)\s*\([^)]*\)\s*\{/);
|
||||
if (componentMatch) {
|
||||
const insertPosition = componentMatch.index + componentMatch[0].length;
|
||||
newContent = newContent.slice(0, insertPosition) + `\n ${TANSTACK_EFFECT}\n` + newContent.slice(insertPosition);
|
||||
} else return {
|
||||
success: false,
|
||||
filePath: rootPath,
|
||||
message: "Could not find a component function in the root file"
|
||||
};
|
||||
return {
|
||||
success: true,
|
||||
filePath: rootPath,
|
||||
message: "Add React Grab",
|
||||
originalContent,
|
||||
newContent
|
||||
};
|
||||
};
|
||||
const hasFrameworkEntryPoint = (projectRoot, framework, nextRouterType) => {
|
||||
switch (framework) {
|
||||
case "next": return nextRouterType === "app" ? findLayoutFile(projectRoot) !== null : findDocumentFile(projectRoot) !== null;
|
||||
case "vite":
|
||||
case "webpack": return findEntryFile(projectRoot) !== null;
|
||||
case "tanstack": return findTanStackRootFile(projectRoot) !== null;
|
||||
default: return false;
|
||||
}
|
||||
};
|
||||
const previewTransform = (projectRoot, framework, nextRouterType, reactGrabAlreadyConfigured = false) => {
|
||||
switch (framework) {
|
||||
case "next":
|
||||
if (nextRouterType === "app") return transformNextAppRouter(projectRoot, reactGrabAlreadyConfigured);
|
||||
return transformNextPagesRouter(projectRoot, reactGrabAlreadyConfigured);
|
||||
case "vite": return transformVite(projectRoot, reactGrabAlreadyConfigured);
|
||||
case "tanstack": return transformTanStack(projectRoot, reactGrabAlreadyConfigured);
|
||||
case "webpack": return transformWebpack(projectRoot, reactGrabAlreadyConfigured);
|
||||
default: return {
|
||||
success: false,
|
||||
filePath: "",
|
||||
message: `Unknown framework: ${framework}. Please add React Grab manually.`
|
||||
};
|
||||
}
|
||||
};
|
||||
const canWriteToFile = (filePath) => {
|
||||
try {
|
||||
accessSync(filePath, constants.W_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const applyTransform = (result) => {
|
||||
if (result.success && result.newContent && result.filePath) {
|
||||
if (!canWriteToFile(result.filePath)) return {
|
||||
success: false,
|
||||
error: `Cannot write to ${result.filePath}. Check file permissions.`
|
||||
};
|
||||
try {
|
||||
writeFileSync(result.filePath, result.newContent);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
||||
};
|
||||
}
|
||||
}
|
||||
return { success: true };
|
||||
};
|
||||
const formatOptionsForNextjs = (options) => {
|
||||
const parts = [];
|
||||
if (options.activationKey) parts.push(`activationKey: ${JSON.stringify(options.activationKey)}`);
|
||||
if (options.activationMode) parts.push(`activationMode: "${options.activationMode}"`);
|
||||
if (options.keyHoldDuration !== void 0) parts.push(`keyHoldDuration: ${options.keyHoldDuration}`);
|
||||
if (options.allowActivationInsideInput !== void 0) parts.push(`allowActivationInsideInput: ${options.allowActivationInsideInput}`);
|
||||
if (options.maxContextLines !== void 0) parts.push(`maxContextLines: ${options.maxContextLines}`);
|
||||
return `{ ${parts.join(", ")} }`;
|
||||
};
|
||||
const formatOptionsAsJson = (options) => {
|
||||
const cleanOptions = {};
|
||||
if (options.activationKey) cleanOptions.activationKey = options.activationKey;
|
||||
if (options.activationMode) cleanOptions.activationMode = options.activationMode;
|
||||
if (options.keyHoldDuration !== void 0) cleanOptions.keyHoldDuration = options.keyHoldDuration;
|
||||
if (options.allowActivationInsideInput !== void 0) cleanOptions.allowActivationInsideInput = options.allowActivationInsideInput;
|
||||
if (options.maxContextLines !== void 0) cleanOptions.maxContextLines = options.maxContextLines;
|
||||
return JSON.stringify(cleanOptions);
|
||||
};
|
||||
const findReactGrabFile = (projectRoot, framework, nextRouterType) => {
|
||||
switch (framework) {
|
||||
case "next": {
|
||||
const primaryFile = nextRouterType === "app" ? findLayoutFile(projectRoot) : findDocumentFile(projectRoot);
|
||||
const primarySetupFile = findFileWithReactGrabSetup(nextRouterType === "app" ? getLayoutFileCandidates(projectRoot) : getDocumentFileCandidates(projectRoot));
|
||||
if (primarySetupFile) return primarySetupFile;
|
||||
const instrumentationFile = findFileWithReactGrabSetup(getInstrumentationFileCandidates(projectRoot));
|
||||
if (instrumentationFile) return instrumentationFile;
|
||||
return primaryFile;
|
||||
}
|
||||
case "vite": {
|
||||
const entryFile = findEntryFile(projectRoot);
|
||||
const entrySetupFile = findFileWithReactGrabSetup(getEntryFileCandidates(projectRoot));
|
||||
if (entrySetupFile) return entrySetupFile;
|
||||
const indexHtml = findFileWithReactGrabSetup(getIndexHtmlCandidates(projectRoot));
|
||||
if (indexHtml) return indexHtml;
|
||||
return entryFile;
|
||||
}
|
||||
case "tanstack": return findFileWithReactGrabSetup(getTanStackRootFileCandidates(projectRoot)) ?? findTanStackRootFile(projectRoot);
|
||||
case "webpack": return findFileWithReactGrabSetup(getEntryFileCandidates(projectRoot)) ?? findEntryFile(projectRoot);
|
||||
default: return null;
|
||||
}
|
||||
};
|
||||
const addOptionsToNextScript = (originalContent, options, filePath) => {
|
||||
const reactGrabScriptMatch = originalContent.match(/(<Script[\s\S]*?react-grab[\s\S]*?)\s*(\/?>)/i);
|
||||
if (!reactGrabScriptMatch) return {
|
||||
success: false,
|
||||
filePath,
|
||||
message: "Could not find React Grab Script tag"
|
||||
};
|
||||
const scriptTag = reactGrabScriptMatch[0];
|
||||
const scriptOpening = reactGrabScriptMatch[1];
|
||||
const scriptClosing = reactGrabScriptMatch[2];
|
||||
const existingDataOptionsMatch = scriptTag.match(/data-options=\{JSON\.stringify\([^)]+\)\}/);
|
||||
const dataOptionsAttr = `data-options={JSON.stringify(\n ${formatOptionsForNextjs(options)}\n )}`;
|
||||
let newScriptTag;
|
||||
if (existingDataOptionsMatch) newScriptTag = scriptTag.replace(existingDataOptionsMatch[0], dataOptionsAttr);
|
||||
else newScriptTag = `${scriptOpening}\n ${dataOptionsAttr}\n ${scriptClosing}`;
|
||||
return {
|
||||
success: true,
|
||||
filePath,
|
||||
message: "Update React Grab options",
|
||||
originalContent,
|
||||
newContent: originalContent.replace(scriptTag, newScriptTag)
|
||||
};
|
||||
};
|
||||
const addOptionsToDynamicImport = (originalContent, options, filePath) => {
|
||||
const reactGrabImportWithInitMatch = originalContent.match(/(void\s+)?import\s*\(\s*["']react-grab(?:\/[^"']+)?["']\s*\)(?:\.then\s*\(\s*(?:\(m\)\s*=>\s*m\.init\s*\([^)]*\)|\(\{\s*init\s*\}\)\s*=>\s*init\s*\([^)]*\))\s*\))?/);
|
||||
if (!reactGrabImportWithInitMatch) return {
|
||||
success: false,
|
||||
filePath,
|
||||
message: "Could not find React Grab import"
|
||||
};
|
||||
const optionsJson = formatOptionsAsJson(options);
|
||||
const newImport = `${reactGrabImportWithInitMatch[1] ?? ""}import("react-grab").then((m) => m.init(${optionsJson}))`;
|
||||
return {
|
||||
success: true,
|
||||
filePath,
|
||||
message: "Update React Grab options",
|
||||
originalContent,
|
||||
newContent: originalContent.replace(reactGrabImportWithInitMatch[0], newImport)
|
||||
};
|
||||
};
|
||||
const addOptionsToTanStackImport = (originalContent, options, filePath) => {
|
||||
const reactGrabImportWithInitMatch = originalContent.match(/(?:(void\s+)?import\s*\(\s*["']react-grab\/core["']\s*\)\.then\s*\(\s*(?:\(\s*\{\s*init\s*\}\s*\)\s*=>\s*init\s*\([^)]*\)|\(m\)\s*=>\s*m\.init\s*\([^)]*\))\s*\)|(void\s+)?import\s*\(\s*["']react-grab(?!\/core)(?:\/[^"']+)?["']\s*\))/);
|
||||
if (!reactGrabImportWithInitMatch) return {
|
||||
success: false,
|
||||
filePath,
|
||||
message: "Could not find React Grab import"
|
||||
};
|
||||
const optionsJson = formatOptionsAsJson(options);
|
||||
const newImport = `${reactGrabImportWithInitMatch[1] ?? reactGrabImportWithInitMatch[2] ?? ""}import("react-grab/core").then(({ init }) => init(${optionsJson}))`;
|
||||
return {
|
||||
success: true,
|
||||
filePath,
|
||||
message: "Update React Grab options",
|
||||
originalContent,
|
||||
newContent: originalContent.replace(reactGrabImportWithInitMatch[0], newImport)
|
||||
};
|
||||
};
|
||||
const addOptionsToAnyImport = (originalContent, options, filePath) => {
|
||||
const dynamicImportResult = addOptionsToDynamicImport(originalContent, options, filePath);
|
||||
if (dynamicImportResult.success) return dynamicImportResult;
|
||||
return addOptionsToTanStackImport(originalContent, options, filePath);
|
||||
};
|
||||
const previewOptionsTransform = (projectRoot, framework, nextRouterType, options) => {
|
||||
const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
|
||||
if (!filePath) return {
|
||||
success: false,
|
||||
filePath: "",
|
||||
message: "Could not find file containing React Grab configuration"
|
||||
};
|
||||
const originalContent = readFileSync(filePath, "utf-8");
|
||||
if (!hasReactGrabSetupCode(originalContent)) return {
|
||||
success: false,
|
||||
filePath,
|
||||
message: "Could not find React Grab code in the file"
|
||||
};
|
||||
switch (framework) {
|
||||
case "next":
|
||||
if (isInstrumentationFile(filePath)) return addOptionsToAnyImport(originalContent, options, filePath);
|
||||
return addOptionsToNextScript(originalContent, options, filePath);
|
||||
case "vite": return addOptionsToDynamicImport(originalContent, options, filePath);
|
||||
case "tanstack": return addOptionsToTanStackImport(originalContent, options, filePath);
|
||||
case "webpack": return addOptionsToDynamicImport(originalContent, options, filePath);
|
||||
default: return {
|
||||
success: false,
|
||||
filePath,
|
||||
message: `Unknown framework: ${framework}`
|
||||
};
|
||||
}
|
||||
};
|
||||
const previewCdnTransform = (projectRoot, framework, nextRouterType, targetCdnDomain) => {
|
||||
const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
|
||||
if (!filePath) return {
|
||||
success: false,
|
||||
filePath: "",
|
||||
message: "Could not find React Grab file"
|
||||
};
|
||||
const originalContent = readFileSync(filePath, "utf-8");
|
||||
const newContent = originalContent.replace(/(https?:)?\/\/[^/\s"']+(?=\/(?:@?react-grab))/g, `//${targetCdnDomain}`).replace(/(https?:)?\/\/[^/\s"']*react-grab[^/\s"']*\.com(?=\/script\.js)/g, `//${targetCdnDomain}`);
|
||||
if (newContent === originalContent) return {
|
||||
success: true,
|
||||
filePath,
|
||||
message: "CDN already set",
|
||||
noChanges: true
|
||||
};
|
||||
return {
|
||||
success: true,
|
||||
filePath,
|
||||
message: "Update CDN",
|
||||
originalContent,
|
||||
newContent
|
||||
};
|
||||
};
|
||||
//#endregion
|
||||
//#region src/utils/install.ts
|
||||
const installPackages = async (packages, options = {}) => {
|
||||
if (packages.length === 0) return;
|
||||
const detectedAgent = options.packageManager ?? await detectPackageManager(options.cwd ?? process.cwd());
|
||||
const args = [];
|
||||
if (options.preferOffline) args.push("--prefer-offline");
|
||||
if (detectedAgent === "pnpm") {
|
||||
args.push("--prod=false");
|
||||
if (existsSync(resolve(options.cwd ?? process.cwd(), "pnpm-workspace.yaml"))) args.push("-w");
|
||||
}
|
||||
if (options.additionalArgs) args.push(...options.additionalArgs);
|
||||
await x(detectedAgent, [
|
||||
detectedAgent === "npm" ? "install" : "add",
|
||||
...options.isDev !== false ? ["-D"] : [],
|
||||
...args,
|
||||
...packages
|
||||
], {
|
||||
nodeOptions: {
|
||||
stdio: options.silent ? "ignore" : "inherit",
|
||||
cwd: options.cwd,
|
||||
env: {
|
||||
...process.env,
|
||||
REACT_GRAB_INIT: "1"
|
||||
}
|
||||
},
|
||||
throwOnError: true
|
||||
});
|
||||
};
|
||||
const getPackagesToInstall = (includeReactGrab = true) => {
|
||||
return includeReactGrab ? ["react-grab"] : [];
|
||||
};
|
||||
//#endregion
|
||||
export { detectReactGrabConfigured as _, previewCdnTransform as a, agentLabel as c, detectAvailableAgents as d, detectFramework as f, detectReactGrab as g, detectProject as h, hasFrameworkEntryPoint as i, installSkill as l, detectPackageManager as m, installPackages as n, previewOptionsTransform as o, detectNextRouterType as p, applyTransform as r, previewTransform as s, getPackagesToInstall as t, removeSkill as u, detectUnsupportedFramework as v, findReactProjects as y };
|
||||
|
||||
//# sourceMappingURL=install-Car5vKPk.js.map
|
||||
1
node_modules/@react-grab/cli/dist/install-Car5vKPk.js.map
generated
vendored
Normal file
1
node_modules/@react-grab/cli/dist/install-Car5vKPk.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1004
node_modules/@react-grab/cli/dist/install-DlD3sLtm.cjs
generated
vendored
Normal file
1004
node_modules/@react-grab/cli/dist/install-DlD3sLtm.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
111
node_modules/@react-grab/cli/dist/read-clipboard.ps1
generated
vendored
Normal file
111
node_modules/@react-grab/cli/dist/read-clipboard.ps1
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
# Windows clipboard reader for `react-grab watch`. Reads CF_UNICODETEXT and the
|
||||
# registered "Chromium Web Custom MIME Data Format" (a base::Pickle of web custom
|
||||
# data) via Win32, in a single OpenClipboard so the two reads are consistent.
|
||||
# Emits { changeCount, text, pickleBase64 } as JSON; the CLI decodes the pickle
|
||||
# (shared with macOS/Linux). GetClipboardSequenceNumber gives a cheap monotonic
|
||||
# change token for idle polling.
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$source = @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public static class RgClip {
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool OpenClipboard(IntPtr hWndNewOwner);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool CloseClipboard();
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetClipboardData(uint uFormat);
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern uint RegisterClipboardFormat(string lpszFormat);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern uint GetClipboardSequenceNumber();
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool IsClipboardFormatAvailable(uint format);
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GlobalLock(IntPtr hMem);
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool GlobalUnlock(IntPtr hMem);
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern UIntPtr GlobalSize(IntPtr hMem);
|
||||
|
||||
private const uint CF_UNICODETEXT = 13;
|
||||
|
||||
private static byte[] ReadLocked(uint format) {
|
||||
if (!IsClipboardFormatAvailable(format)) return null;
|
||||
IntPtr handle = GetClipboardData(format);
|
||||
if (handle == IntPtr.Zero) return null;
|
||||
IntPtr pointer = GlobalLock(handle);
|
||||
if (pointer == IntPtr.Zero) return null;
|
||||
try {
|
||||
ulong size = GlobalSize(handle).ToUInt64();
|
||||
if (size == 0 || size > int.MaxValue) return null;
|
||||
byte[] bytes = new byte[(int)size];
|
||||
Marshal.Copy(pointer, bytes, 0, (int)size);
|
||||
return bytes;
|
||||
} finally {
|
||||
GlobalUnlock(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[][] ReadAll(uint customFormat) {
|
||||
byte[][] result = new byte[2][];
|
||||
if (!OpenClipboard(IntPtr.Zero)) return result;
|
||||
try {
|
||||
result[0] = ReadLocked(CF_UNICODETEXT);
|
||||
result[1] = ReadLocked(customFormat);
|
||||
} finally {
|
||||
CloseClipboard();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
# Add-Type recompiles on every process start, so the reader is compiled to a
|
||||
# cached assembly once and merely loaded on subsequent polls.
|
||||
$cacheDir = Join-Path $env:TEMP "react-grab-watch"
|
||||
# Key the cached DLL by a hash of the source so a changed reader recompiles
|
||||
# instead of loading a stale assembly.
|
||||
$sourceHashBytes = [System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($source))
|
||||
$sourceHash = ([System.BitConverter]::ToString($sourceHashBytes) -replace "-", "").Substring(0, 16)
|
||||
$cachedDll = Join-Path $cacheDir "RgClipReader-$sourceHash.dll"
|
||||
$loaded = $false
|
||||
if (Test-Path $cachedDll) {
|
||||
try { Add-Type -Path $cachedDll | Out-Null; $loaded = $true } catch {}
|
||||
}
|
||||
if (-not $loaded) {
|
||||
try {
|
||||
New-Item -ItemType Directory -Force -Path $cacheDir | Out-Null
|
||||
Add-Type -TypeDefinition $source -Language CSharp -OutputAssembly $cachedDll -ErrorAction Stop | Out-Null
|
||||
Add-Type -Path $cachedDll | Out-Null
|
||||
$loaded = $true
|
||||
} catch {}
|
||||
}
|
||||
if (-not $loaded) {
|
||||
Add-Type -TypeDefinition $source -Language CSharp | Out-Null
|
||||
}
|
||||
|
||||
$changeCount = [RgClip]::GetClipboardSequenceNumber()
|
||||
$customFormat = [RgClip]::RegisterClipboardFormat("Chromium Web Custom MIME Data Format")
|
||||
$blobs = [RgClip]::ReadAll($customFormat)
|
||||
|
||||
$text = $null
|
||||
if ($null -ne $blobs[0]) {
|
||||
$text = [System.Text.Encoding]::Unicode.GetString($blobs[0]).TrimEnd([char]0)
|
||||
}
|
||||
|
||||
$pickleBase64 = $null
|
||||
if ($null -ne $blobs[1]) {
|
||||
$pickleBase64 = [System.Convert]::ToBase64String($blobs[1])
|
||||
}
|
||||
|
||||
$payload = [ordered]@{
|
||||
changeCount = [int64]$changeCount
|
||||
text = $text
|
||||
pickleBase64 = $pickleBase64
|
||||
}
|
||||
|
||||
$payload | ConvertTo-Json -Compress
|
||||
35
node_modules/@react-grab/cli/dist/read-clipboard.swift
generated
vendored
Normal file
35
node_modules/@react-grab/cli/dist/read-clipboard.swift
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
import AppKit
|
||||
|
||||
// macOS clipboard reader for `react-grab watch`. React Grab's custom MIME type
|
||||
// is not exposed directly by Chromium-based browsers: the legacy
|
||||
// execCommand("copy") + dataTransfer.setData path lands as a base::Pickle under
|
||||
// "org.chromium.web-custom-data"; the async Clipboard API path lands as raw
|
||||
// bytes referenced by "org.w3.web-custom-format.map". This reader emits the
|
||||
// pickle as base64 (the CLI decodes it, shared with Linux/Windows) and the W3C
|
||||
// payload as a resolved string, plus changeCount for cheap idle polling.
|
||||
|
||||
let GRAB_MIME = "application/x-react-grab"
|
||||
let pasteboard = NSPasteboard.general
|
||||
var result: [String: Any] = ["changeCount": pasteboard.changeCount]
|
||||
|
||||
if let text = pasteboard.string(forType: .string) {
|
||||
result["text"] = text
|
||||
}
|
||||
|
||||
if let data = pasteboard.data(forType: NSPasteboard.PasteboardType("org.chromium.web-custom-data")) {
|
||||
result["pickleBase64"] = data.base64EncodedString()
|
||||
}
|
||||
|
||||
if let mapData = pasteboard.data(
|
||||
forType: NSPasteboard.PasteboardType("org.w3.web-custom-format.map")),
|
||||
let mapString = String(data: mapData, encoding: .utf8),
|
||||
let mapJson = try? JSONSerialization.jsonObject(with: Data(mapString.utf8)) as? [String: String],
|
||||
let pasteboardType = mapJson["web " + GRAB_MIME] ?? mapJson[GRAB_MIME],
|
||||
let raw = pasteboard.data(forType: NSPasteboard.PasteboardType(pasteboardType)),
|
||||
let value = String(data: raw, encoding: .utf8)
|
||||
{
|
||||
result["grab"] = value
|
||||
}
|
||||
|
||||
let outData = (try? JSONSerialization.data(withJSONObject: result, options: [])) ?? Data("{}".utf8)
|
||||
FileHandle.standardOutput.write(outData)
|
||||
21
node_modules/@react-grab/cli/node_modules/ignore/LICENSE-MIT
generated
vendored
Normal file
21
node_modules/@react-grab/cli/node_modules/ignore/LICENSE-MIT
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright (c) 2013 Kael Zhang <i@kael.me>, contributors
|
||||
http://kael.me/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
452
node_modules/@react-grab/cli/node_modules/ignore/README.md
generated
vendored
Normal file
452
node_modules/@react-grab/cli/node_modules/ignore/README.md
generated
vendored
Normal file
@@ -0,0 +1,452 @@
|
||||
| Linux / MacOS / Windows | Coverage | Downloads |
|
||||
| ----------------------- | -------- | --------- |
|
||||
| [![build][bb]][bl] | [![coverage][cb]][cl] | [![downloads][db]][dl] |
|
||||
|
||||
[bb]: https://github.com/kaelzhang/node-ignore/actions/workflows/nodejs.yml/badge.svg
|
||||
[bl]: https://github.com/kaelzhang/node-ignore/actions/workflows/nodejs.yml
|
||||
|
||||
[cb]: https://codecov.io/gh/kaelzhang/node-ignore/branch/master/graph/badge.svg
|
||||
[cl]: https://codecov.io/gh/kaelzhang/node-ignore
|
||||
|
||||
[db]: http://img.shields.io/npm/dm/ignore.svg
|
||||
[dl]: https://www.npmjs.org/package/ignore
|
||||
|
||||
# ignore
|
||||
|
||||
`ignore` is a manager, filter and parser which implemented in pure JavaScript according to the [.gitignore spec 2.22.1](http://git-scm.com/docs/gitignore).
|
||||
|
||||
`ignore` is used by eslint, gitbook and [many others](https://www.npmjs.com/browse/depended/ignore).
|
||||
|
||||
Pay **ATTENTION** that [`minimatch`](https://www.npmjs.org/package/minimatch) (which used by `fstream-ignore`) does not follow the gitignore spec.
|
||||
|
||||
To filter filenames according to a .gitignore file, I recommend this npm package, `ignore`.
|
||||
|
||||
To parse an `.npmignore` file, you should use `minimatch`, because an `.npmignore` file is parsed by npm using `minimatch` and it does not work in the .gitignore way.
|
||||
|
||||
### Tested on
|
||||
|
||||
`ignore` is fully tested, and has more than **five hundreds** of unit tests.
|
||||
|
||||
- Linux + Node: `0.8` - `7.x`
|
||||
- Windows + Node: `0.10` - `7.x`, node < `0.10` is not tested due to the lack of support of appveyor.
|
||||
|
||||
Actually, `ignore` does not rely on any versions of node specially.
|
||||
|
||||
Since `4.0.0`, ignore will no longer support `node < 6` by default, to use in node < 6, `require('ignore/legacy')`. For details, see [CHANGELOG](https://github.com/kaelzhang/node-ignore/blob/master/CHANGELOG.md).
|
||||
|
||||
## Table Of Main Contents
|
||||
|
||||
- [Usage](#usage)
|
||||
- [`Pathname` Conventions](#pathname-conventions)
|
||||
- See Also:
|
||||
- [`glob-gitignore`](https://www.npmjs.com/package/glob-gitignore) matches files using patterns and filters them according to gitignore rules.
|
||||
- [Upgrade Guide](#upgrade-guide)
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm i ignore
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import ignore from 'ignore'
|
||||
const ig = ignore().add(['.abc/*', '!.abc/d/'])
|
||||
```
|
||||
|
||||
### Filter the given paths
|
||||
|
||||
```js
|
||||
const paths = [
|
||||
'.abc/a.js', // filtered out
|
||||
'.abc/d/e.js' // included
|
||||
]
|
||||
|
||||
ig.filter(paths) // ['.abc/d/e.js']
|
||||
ig.ignores('.abc/a.js') // true
|
||||
```
|
||||
|
||||
### As the filter function
|
||||
|
||||
```js
|
||||
paths.filter(ig.createFilter()); // ['.abc/d/e.js']
|
||||
```
|
||||
|
||||
### Win32 paths will be handled
|
||||
|
||||
```js
|
||||
ig.filter(['.abc\\a.js', '.abc\\d\\e.js'])
|
||||
// if the code above runs on windows, the result will be
|
||||
// ['.abc\\d\\e.js']
|
||||
```
|
||||
|
||||
## Why another ignore?
|
||||
|
||||
- `ignore` is a standalone module, and is much simpler so that it could easy work with other programs, unlike [isaacs](https://npmjs.org/~isaacs)'s [fstream-ignore](https://npmjs.org/package/fstream-ignore) which must work with the modules of the fstream family.
|
||||
|
||||
- `ignore` only contains utility methods to filter paths according to the specified ignore rules, so
|
||||
- `ignore` never try to find out ignore rules by traversing directories or fetching from git configurations.
|
||||
- `ignore` don't cares about sub-modules of git projects.
|
||||
|
||||
- Exactly according to [gitignore man page](http://git-scm.com/docs/gitignore), fixes some known matching issues of fstream-ignore, such as:
|
||||
- '`/*.js`' should only match '`a.js`', but not '`abc/a.js`'.
|
||||
- '`**/foo`' should match '`foo`' anywhere.
|
||||
- Prevent re-including a file if a parent directory of that file is excluded.
|
||||
- Handle trailing whitespaces:
|
||||
- `'a '`(one space) should not match `'a '`(two spaces).
|
||||
- `'a \ '` matches `'a '`
|
||||
- All test cases are verified with the result of `git check-ignore`.
|
||||
|
||||
# Methods
|
||||
|
||||
## .add(pattern: string | Ignore): this
|
||||
## .add(patterns: Array<string | Ignore>): this
|
||||
## .add({pattern: string, mark?: string}): this since 7.0.0
|
||||
|
||||
- **pattern** `string | Ignore` An ignore pattern string, or the `Ignore` instance
|
||||
- **patterns** `Array<string | Ignore>` Array of ignore patterns.
|
||||
- **mark?** `string` Pattern mark, which is used to associate the pattern with a certain marker, such as the line no of the `.gitignore` file. Actually it could be an arbitrary string and is optional.
|
||||
|
||||
Adds a rule or several rules to the current manager.
|
||||
|
||||
Returns `this`
|
||||
|
||||
Notice that a line starting with `'#'`(hash) is treated as a comment. Put a backslash (`'\'`) in front of the first hash for patterns that begin with a hash, if you want to ignore a file with a hash at the beginning of the filename.
|
||||
|
||||
```js
|
||||
ignore().add('#abc').ignores('#abc') // false
|
||||
ignore().add('\\#abc').ignores('#abc') // true
|
||||
```
|
||||
|
||||
`pattern` could either be a line of ignore pattern or a string of multiple ignore patterns, which means we could just `ignore().add()` the content of a ignore file:
|
||||
|
||||
```js
|
||||
ignore()
|
||||
.add(fs.readFileSync(filenameOfGitignore).toString())
|
||||
.filter(filenames)
|
||||
```
|
||||
|
||||
`pattern` could also be an `ignore` instance, so that we could easily inherit the rules of another `Ignore` instance.
|
||||
|
||||
## .ignores(pathname: [Pathname](#pathname-conventions)): boolean
|
||||
|
||||
> new in 3.2.0
|
||||
|
||||
Returns `Boolean` whether `pathname` should be ignored.
|
||||
|
||||
```js
|
||||
ig.ignores('.abc/a.js') // true
|
||||
```
|
||||
|
||||
Please **PAY ATTENTION** that `.ignores()` is **NOT** equivalent to `git check-ignore` although in most cases they return equivalent results.
|
||||
|
||||
However, for the purposes of imitating the behavior of `git check-ignore`, please use `.checkIgnore()` instead.
|
||||
|
||||
### `Pathname` Conventions:
|
||||
|
||||
#### 1. `Pathname` should be a `path.relative()`d pathname
|
||||
|
||||
`Pathname` should be a string that have been `path.join()`ed, or the return value of `path.relative()` to the current directory,
|
||||
|
||||
```js
|
||||
// WRONG, an error will be thrown
|
||||
ig.ignores('./abc')
|
||||
|
||||
// WRONG, for it will never happen, and an error will be thrown
|
||||
// If the gitignore rule locates at the root directory,
|
||||
// `'/abc'` should be changed to `'abc'`.
|
||||
// ```
|
||||
// path.relative('/', '/abc') -> 'abc'
|
||||
// ```
|
||||
ig.ignores('/abc')
|
||||
|
||||
// WRONG, that it is an absolute path on Windows, an error will be thrown
|
||||
ig.ignores('C:\\abc')
|
||||
|
||||
// Right
|
||||
ig.ignores('abc')
|
||||
|
||||
// Right
|
||||
ig.ignores(path.join('./abc')) // path.join('./abc') -> 'abc'
|
||||
```
|
||||
|
||||
In other words, each `Pathname` here should be a relative path to the directory of the gitignore rules.
|
||||
|
||||
Suppose the dir structure is:
|
||||
|
||||
```
|
||||
/path/to/your/repo
|
||||
|-- a
|
||||
| |-- a.js
|
||||
|
|
||||
|-- .b
|
||||
|
|
||||
|-- .c
|
||||
|-- .DS_store
|
||||
```
|
||||
|
||||
Then the `paths` might be like this:
|
||||
|
||||
```js
|
||||
[
|
||||
'a/a.js'
|
||||
'.b',
|
||||
'.c/.DS_store'
|
||||
]
|
||||
```
|
||||
|
||||
#### 2. filenames and dirnames
|
||||
|
||||
`node-ignore` does NO `fs.stat` during path matching, so `node-ignore` treats
|
||||
- `foo` as a file
|
||||
- **`foo/` as a directory**
|
||||
|
||||
For the example below:
|
||||
|
||||
```js
|
||||
// First, we add a ignore pattern to ignore a directory
|
||||
ig.add('config/')
|
||||
|
||||
// `ig` does NOT know if 'config', in the real world,
|
||||
// is a normal file, directory or something.
|
||||
|
||||
ig.ignores('config')
|
||||
// `ig` treats `config` as a file, so it returns `false`
|
||||
|
||||
ig.ignores('config/')
|
||||
// returns `true`
|
||||
```
|
||||
|
||||
Specially for people who develop some library based on `node-ignore`, it is important to understand that.
|
||||
|
||||
Usually, you could use [`glob`](http://npmjs.org/package/glob) with `option.mark = true` to fetch the structure of the current directory:
|
||||
|
||||
```js
|
||||
import glob from 'glob'
|
||||
|
||||
glob('**', {
|
||||
// Adds a / character to directory matches.
|
||||
mark: true
|
||||
}, (err, files) => {
|
||||
if (err) {
|
||||
return console.error(err)
|
||||
}
|
||||
|
||||
let filtered = ignore().add(patterns).filter(files)
|
||||
console.log(filtered)
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## .filter(paths: Array<Pathname>): Array<Pathname>
|
||||
|
||||
```ts
|
||||
type Pathname = string
|
||||
```
|
||||
|
||||
Filters the given array of pathnames, and returns the filtered array.
|
||||
|
||||
- **paths** `Array.<Pathname>` The array of `pathname`s to be filtered.
|
||||
|
||||
## .createFilter()
|
||||
|
||||
Creates a filter function which could filter an array of paths with `Array.prototype.filter`.
|
||||
|
||||
Returns `function(path)` the filter function.
|
||||
|
||||
## .test(pathname: Pathname): TestResult
|
||||
|
||||
> New in 5.0.0
|
||||
|
||||
Returns `TestResult`
|
||||
|
||||
```ts
|
||||
// Since 5.0.0
|
||||
interface TestResult {
|
||||
ignored: boolean
|
||||
// true if the `pathname` is finally unignored by some negative pattern
|
||||
unignored: boolean
|
||||
// The `IgnoreRule` which ignores the pathname
|
||||
rule?: IgnoreRule
|
||||
}
|
||||
|
||||
// Since 7.0.0
|
||||
interface IgnoreRule {
|
||||
// The original pattern
|
||||
pattern: string
|
||||
// Whether the pattern is a negative pattern
|
||||
negative: boolean
|
||||
// Which is used for other packages to build things upon `node-ignore`
|
||||
mark?: string
|
||||
}
|
||||
```
|
||||
|
||||
- `{ignored: true, unignored: false}`: the `pathname` is ignored
|
||||
- `{ignored: false, unignored: true}`: the `pathname` is unignored
|
||||
- `{ignored: false, unignored: false}`: the `pathname` is never matched by any ignore rules.
|
||||
|
||||
## .checkIgnore(target: string): TestResult
|
||||
|
||||
> new in 7.0.0
|
||||
|
||||
Debugs gitignore / exclude files, which is equivalent to `git check-ignore -v`. Usually this method is used for other packages to implement the function of `git check-ignore -v` upon `node-ignore`
|
||||
|
||||
- **target** `string` the target to test.
|
||||
|
||||
Returns `TestResult`
|
||||
|
||||
```js
|
||||
ig.add({
|
||||
pattern: 'foo/*',
|
||||
mark: '60'
|
||||
})
|
||||
|
||||
const {
|
||||
ignored,
|
||||
rule
|
||||
} = checkIgnore('foo/')
|
||||
|
||||
if (ignored) {
|
||||
console.log(`.gitignore:${result}:${rule.mark}:${rule.pattern} foo/`)
|
||||
}
|
||||
|
||||
// .gitignore:60:foo/* foo/
|
||||
```
|
||||
|
||||
Please pay attention that this method does not have a strong built-in cache mechanism.
|
||||
|
||||
The purpose of introducing this method is to make it possible to implement the `git check-ignore` command in JavaScript based on `node-ignore`.
|
||||
|
||||
So do not use this method in those situations where performance is extremely important.
|
||||
|
||||
## static `isPathValid(pathname): boolean` since 5.0.0
|
||||
|
||||
Check whether the `pathname` is an valid `path.relative()`d path according to the [convention](#1-pathname-should-be-a-pathrelatived-pathname).
|
||||
|
||||
This method is **NOT** used to check if an ignore pattern is valid.
|
||||
|
||||
```js
|
||||
import {isPathValid} from 'ignore'
|
||||
|
||||
isPathValid('./foo') // false
|
||||
```
|
||||
|
||||
## <strike>.addIgnoreFile(path)</strike>
|
||||
|
||||
REMOVED in `3.x` for now.
|
||||
|
||||
To upgrade `ignore@2.x` up to `3.x`, use
|
||||
|
||||
```js
|
||||
import fs from 'fs'
|
||||
|
||||
if (fs.existsSync(filename)) {
|
||||
ignore().add(fs.readFileSync(filename).toString())
|
||||
}
|
||||
```
|
||||
|
||||
instead.
|
||||
|
||||
## ignore(options)
|
||||
|
||||
### `options.ignorecase` since 4.0.0
|
||||
|
||||
Similar to the `core.ignorecase` option of [git-config](https://git-scm.com/docs/git-config), `node-ignore` will be case insensitive if `options.ignorecase` is set to `true` (the default value), otherwise case sensitive.
|
||||
|
||||
```js
|
||||
const ig = ignore({
|
||||
ignorecase: false
|
||||
})
|
||||
|
||||
ig.add('*.png')
|
||||
|
||||
ig.ignores('*.PNG') // false
|
||||
```
|
||||
|
||||
### `options.ignoreCase?: boolean` since 5.2.0
|
||||
|
||||
Which is an alternative to `options.ignoreCase`
|
||||
|
||||
### `options.allowRelativePaths?: boolean` since 5.2.0
|
||||
|
||||
This option brings backward compatibility with projects which based on `ignore@4.x`. If `options.allowRelativePaths` is `true`, `ignore` will not check whether the given path to be tested is [`path.relative()`d](#pathname-conventions).
|
||||
|
||||
However, passing a relative path, such as `'./foo'` or `'../foo'`, to test if it is ignored or not is not a good practise, which might lead to unexpected behavior
|
||||
|
||||
```js
|
||||
ignore({
|
||||
allowRelativePaths: true
|
||||
}).ignores('../foo/bar.js') // And it will not throw
|
||||
```
|
||||
|
||||
****
|
||||
|
||||
# Upgrade Guide
|
||||
|
||||
## Upgrade 4.x -> 5.x
|
||||
|
||||
Since `5.0.0`, if an invalid `Pathname` passed into `ig.ignores()`, an error will be thrown, unless `options.allowRelative = true` is passed to the `Ignore` factory.
|
||||
|
||||
While `ignore < 5.0.0` did not make sure what the return value was, as well as
|
||||
|
||||
```ts
|
||||
.ignores(pathname: Pathname): boolean
|
||||
|
||||
.filter(pathnames: Array<Pathname>): Array<Pathname>
|
||||
|
||||
.createFilter(): (pathname: Pathname) => boolean
|
||||
|
||||
.test(pathname: Pathname): {ignored: boolean, unignored: boolean}
|
||||
```
|
||||
|
||||
See the convention [here](#1-pathname-should-be-a-pathrelatived-pathname) for details.
|
||||
|
||||
If there are invalid pathnames, the conversion and filtration should be done by users.
|
||||
|
||||
```js
|
||||
import {isPathValid} from 'ignore' // introduced in 5.0.0
|
||||
|
||||
const paths = [
|
||||
// invalid
|
||||
//////////////////
|
||||
'',
|
||||
false,
|
||||
'../foo',
|
||||
'.',
|
||||
//////////////////
|
||||
|
||||
// valid
|
||||
'foo'
|
||||
]
|
||||
.filter(isPathValid)
|
||||
|
||||
ig.filter(paths)
|
||||
```
|
||||
|
||||
## Upgrade 3.x -> 4.x
|
||||
|
||||
Since `4.0.0`, `ignore` will no longer support node < 6, to use `ignore` in node < 6:
|
||||
|
||||
```js
|
||||
var ignore = require('ignore/legacy')
|
||||
```
|
||||
|
||||
## Upgrade 2.x -> 3.x
|
||||
|
||||
- All `options` of 2.x are unnecessary and removed, so just remove them.
|
||||
- `ignore()` instance is no longer an [`EventEmitter`](nodejs.org/api/events.html), and all events are unnecessary and removed.
|
||||
- `.addIgnoreFile()` is removed, see the [.addIgnoreFile](#addignorefilepath) section for details.
|
||||
|
||||
****
|
||||
|
||||
# Collaborators
|
||||
|
||||
- [@whitecolor](https://github.com/whitecolor) *Alex*
|
||||
- [@SamyPesse](https://github.com/SamyPesse) *Samy Pessé*
|
||||
- [@azproduction](https://github.com/azproduction) *Mikhail Davydov*
|
||||
- [@TrySound](https://github.com/TrySound) *Bogdan Chadkin*
|
||||
- [@JanMattner](https://github.com/JanMattner) *Jan Mattner*
|
||||
- [@ntwb](https://github.com/ntwb) *Stephen Edgar*
|
||||
- [@kasperisager](https://github.com/kasperisager) *Kasper Isager*
|
||||
- [@sandersn](https://github.com/sandersn) *Nathan Shively-Sanders*
|
||||
81
node_modules/@react-grab/cli/node_modules/ignore/index.d.ts
generated
vendored
Normal file
81
node_modules/@react-grab/cli/node_modules/ignore/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
type Pathname = string
|
||||
|
||||
interface IgnoreRule {
|
||||
pattern: string
|
||||
mark?: string
|
||||
negative: boolean
|
||||
}
|
||||
|
||||
interface TestResult {
|
||||
ignored: boolean
|
||||
unignored: boolean
|
||||
rule?: IgnoreRule
|
||||
}
|
||||
|
||||
interface PatternParams {
|
||||
pattern: string
|
||||
mark?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new ignore manager.
|
||||
*/
|
||||
declare function ignore(options?: ignore.Options): ignore.Ignore
|
||||
declare namespace ignore {
|
||||
interface Ignore {
|
||||
/**
|
||||
* Adds one or several rules to the current manager.
|
||||
* @param {string[]} patterns
|
||||
* @returns IgnoreBase
|
||||
*/
|
||||
add(
|
||||
patterns: string | Ignore | readonly (string | Ignore)[] | PatternParams
|
||||
): this
|
||||
|
||||
/**
|
||||
* Filters the given array of pathnames, and returns the filtered array.
|
||||
* NOTICE that each path here should be a relative path to the root of your repository.
|
||||
* @param paths the array of paths to be filtered.
|
||||
* @returns The filtered array of paths
|
||||
*/
|
||||
filter(pathnames: readonly Pathname[]): Pathname[]
|
||||
|
||||
/**
|
||||
* Creates a filter function which could filter
|
||||
* an array of paths with Array.prototype.filter.
|
||||
*/
|
||||
createFilter(): (pathname: Pathname) => boolean
|
||||
|
||||
/**
|
||||
* Returns Boolean whether pathname should be ignored.
|
||||
* @param {string} pathname a path to check
|
||||
* @returns boolean
|
||||
*/
|
||||
ignores(pathname: Pathname): boolean
|
||||
|
||||
/**
|
||||
* Returns whether pathname should be ignored or unignored
|
||||
* @param {string} pathname a path to check
|
||||
* @returns TestResult
|
||||
*/
|
||||
test(pathname: Pathname): TestResult
|
||||
|
||||
/**
|
||||
* Debugs ignore rules and returns the checking result, which is
|
||||
* equivalent to `git check-ignore -v`.
|
||||
* @returns TestResult
|
||||
*/
|
||||
checkIgnore(pathname: Pathname): TestResult
|
||||
}
|
||||
|
||||
interface Options {
|
||||
ignorecase?: boolean
|
||||
// For compatibility
|
||||
ignoreCase?: boolean
|
||||
allowRelativePaths?: boolean
|
||||
}
|
||||
|
||||
function isPathValid(pathname: string): boolean
|
||||
}
|
||||
|
||||
export = ignore
|
||||
793
node_modules/@react-grab/cli/node_modules/ignore/index.js
generated
vendored
Normal file
793
node_modules/@react-grab/cli/node_modules/ignore/index.js
generated
vendored
Normal file
@@ -0,0 +1,793 @@
|
||||
// A simple implementation of make-array
|
||||
function makeArray (subject) {
|
||||
return Array.isArray(subject)
|
||||
? subject
|
||||
: [subject]
|
||||
}
|
||||
|
||||
const UNDEFINED = undefined
|
||||
const EMPTY = ''
|
||||
const SPACE = ' '
|
||||
const ESCAPE = '\\'
|
||||
const REGEX_TEST_BLANK_LINE = /^\s+$/
|
||||
const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/
|
||||
const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/
|
||||
const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/
|
||||
const REGEX_SPLITALL_CRLF = /\r?\n/g
|
||||
|
||||
// Invalid:
|
||||
// - /foo,
|
||||
// - ./foo,
|
||||
// - ../foo,
|
||||
// - .
|
||||
// - ..
|
||||
// Valid:
|
||||
// - .foo
|
||||
const REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/
|
||||
|
||||
const REGEX_TEST_TRAILING_SLASH = /\/$/
|
||||
|
||||
const SLASH = '/'
|
||||
|
||||
// Do not use ternary expression here, since "istanbul ignore next" is buggy
|
||||
let TMP_KEY_IGNORE = 'node-ignore'
|
||||
/* istanbul ignore else */
|
||||
if (typeof Symbol !== 'undefined') {
|
||||
TMP_KEY_IGNORE = Symbol.for('node-ignore')
|
||||
}
|
||||
const KEY_IGNORE = TMP_KEY_IGNORE
|
||||
|
||||
const define = (object, key, value) => {
|
||||
Object.defineProperty(object, key, {value})
|
||||
return value
|
||||
}
|
||||
|
||||
const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g
|
||||
|
||||
const RETURN_FALSE = () => false
|
||||
|
||||
// Sanitize the range of a regular expression
|
||||
// The cases are complicated, see test cases for details
|
||||
const sanitizeRange = range => range.replace(
|
||||
REGEX_REGEXP_RANGE,
|
||||
(match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0)
|
||||
? match
|
||||
// Invalid range (out of order) which is ok for gitignore rules but
|
||||
// fatal for JavaScript regular expression, so eliminate it.
|
||||
: EMPTY
|
||||
)
|
||||
|
||||
// > An optional `!` or `^` at the start of a class negates it, so that it
|
||||
// > matches any character not in the set. (gitignore(5), fnmatch(3))
|
||||
// The leading `^` has already been escaped to `\^` by the metacharacter
|
||||
// escaper, so we strip the literal `!` or escaped `^` and emit a single
|
||||
// regex `^` which is the JavaScript negation token.
|
||||
const negateRange = range => range.startsWith('!') || range.startsWith('\\^')
|
||||
? `^${range.slice(range[0] === '!' ? 1 : 2)}`
|
||||
: range
|
||||
|
||||
// See fixtures #59
|
||||
const cleanRangeBackSlash = slashes => {
|
||||
const {length} = slashes
|
||||
return slashes.slice(0, length - length % 2)
|
||||
}
|
||||
|
||||
// > If the pattern ends with a slash,
|
||||
// > it is removed for the purpose of the following description,
|
||||
// > but it would only find a match with a directory.
|
||||
// > In other words, foo/ will match a directory foo and paths underneath it,
|
||||
// > but will not match a regular file or a symbolic link foo
|
||||
// > (this is consistent with the way how pathspec works in general in Git).
|
||||
// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
|
||||
// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
|
||||
// you could use option `mark: true` with `glob`
|
||||
|
||||
// '`foo/`' should not continue with the '`..`'
|
||||
const REPLACERS = [
|
||||
|
||||
[
|
||||
// Remove BOM
|
||||
// TODO:
|
||||
// Other similar zero-width characters?
|
||||
/^\uFEFF/,
|
||||
() => EMPTY
|
||||
],
|
||||
|
||||
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
||||
[
|
||||
// (a\ ) -> (a )
|
||||
// (a ) -> (a)
|
||||
// (a ) -> (a)
|
||||
// (a \ ) -> (a )
|
||||
/((?:\\\\)*?)(\\?\s+)$/,
|
||||
(_, m1, m2) => m1 + (
|
||||
m2.indexOf('\\') === 0
|
||||
? SPACE
|
||||
: EMPTY
|
||||
)
|
||||
],
|
||||
|
||||
// Replace (\ ) with ' '
|
||||
// (\ ) -> ' '
|
||||
// (\\ ) -> '\\ '
|
||||
// (\\\ ) -> '\\ '
|
||||
[
|
||||
/(\\+?)\s/g,
|
||||
(_, m1) => {
|
||||
const {length} = m1
|
||||
return m1.slice(0, length - length % 2) + SPACE
|
||||
}
|
||||
],
|
||||
|
||||
// Escape metacharacters
|
||||
// which is written down by users but means special for regular expressions.
|
||||
|
||||
// > There are 12 characters with special meanings:
|
||||
// > - the backslash \,
|
||||
// > - the caret ^,
|
||||
// > - the dollar sign $,
|
||||
// > - the period or dot .,
|
||||
// > - the vertical bar or pipe symbol |,
|
||||
// > - the question mark ?,
|
||||
// > - the asterisk or star *,
|
||||
// > - the plus sign +,
|
||||
// > - the opening parenthesis (,
|
||||
// > - the closing parenthesis ),
|
||||
// > - and the opening square bracket [,
|
||||
// > - the opening curly brace {,
|
||||
// > These special characters are often called "metacharacters".
|
||||
[
|
||||
/[\\$.|*+(){^]/g,
|
||||
match => `\\${match}`
|
||||
],
|
||||
|
||||
[
|
||||
// > a question mark (?) matches a single character
|
||||
/(?!\\)\?/g,
|
||||
() => '[^/]'
|
||||
],
|
||||
|
||||
// leading slash
|
||||
[
|
||||
|
||||
// > A leading slash matches the beginning of the pathname.
|
||||
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
||||
// A leading slash matches the beginning of the pathname
|
||||
/^\//,
|
||||
() => '^'
|
||||
],
|
||||
|
||||
// replace special metacharacter slash after the leading slash
|
||||
[
|
||||
/\//g,
|
||||
() => '\\/'
|
||||
],
|
||||
|
||||
[
|
||||
// > A leading "**" followed by a slash means match in all directories.
|
||||
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
||||
// > the same as pattern "foo".
|
||||
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
||||
// > under directory "foo".
|
||||
// Notice that the '*'s have been replaced as '\\*'
|
||||
/^\^*(?:\\\*\\\*\\\/)+/,
|
||||
|
||||
// '**/foo' <-> 'foo'
|
||||
() => '^(?:.*\\/)?'
|
||||
],
|
||||
|
||||
// starting
|
||||
[
|
||||
// there will be no leading '/'
|
||||
// (which has been replaced by section "leading slash")
|
||||
// If starts with '**', adding a '^' to the regular expression also works
|
||||
/^(?=[^^])/,
|
||||
function startingReplacer () {
|
||||
// If has a slash `/` at the beginning or middle
|
||||
return !/\/(?!$)/.test(this)
|
||||
// > Prior to 2.22.1
|
||||
// > If the pattern does not contain a slash /,
|
||||
// > Git treats it as a shell glob pattern
|
||||
// Actually, if there is only a trailing slash,
|
||||
// git also treats it as a shell glob pattern
|
||||
|
||||
// After 2.22.1 (compatible but clearer)
|
||||
// > If there is a separator at the beginning or middle (or both)
|
||||
// > of the pattern, then the pattern is relative to the directory
|
||||
// > level of the particular .gitignore file itself.
|
||||
// > Otherwise the pattern may also match at any level below
|
||||
// > the .gitignore level.
|
||||
? '(?:^|\\/)'
|
||||
|
||||
// > Otherwise, Git treats the pattern as a shell glob suitable for
|
||||
// > consumption by fnmatch(3)
|
||||
: '^'
|
||||
}
|
||||
],
|
||||
|
||||
// two globstars
|
||||
[
|
||||
// Use lookahead assertions so that we could match more than one `'/**'`
|
||||
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
||||
|
||||
// Zero, one or several directories
|
||||
// should not use '*', or it will be replaced by the next replacer
|
||||
|
||||
// Check if it is not the last `'/**'`
|
||||
(_, index, str) => index + 6 < str.length
|
||||
|
||||
// case: /**/
|
||||
// > A slash followed by two consecutive asterisks then a slash matches
|
||||
// > zero or more directories.
|
||||
// > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
|
||||
// '/**/'
|
||||
? '(?:\\/[^\\/]+)*'
|
||||
|
||||
// case: /**
|
||||
// > A trailing `"/**"` matches everything inside.
|
||||
|
||||
// #21: everything inside but it should not include the current folder
|
||||
: '\\/.+'
|
||||
],
|
||||
|
||||
// normal intermediate wildcards
|
||||
[
|
||||
// Never replace escaped '*'
|
||||
// ignore rule '\*' will match the path '*'
|
||||
|
||||
// 'abc.*/' -> go
|
||||
// 'abc.*' -> skip this rule,
|
||||
// coz trailing single wildcard will be handed by [trailing wildcard]
|
||||
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
||||
|
||||
// '*.js' matches '.js'
|
||||
// '*.js' doesn't match 'abc'
|
||||
(_, p1, p2) => {
|
||||
// 1.
|
||||
// > An asterisk "*" matches anything except a slash.
|
||||
// 2.
|
||||
// > Other consecutive asterisks are considered regular asterisks
|
||||
// > and will match according to the previous rules.
|
||||
const unescaped = p2.replace(/\\\*/g, '[^\\/]*')
|
||||
return p1 + unescaped
|
||||
}
|
||||
],
|
||||
|
||||
[
|
||||
// unescape, revert step 3 except for back slash
|
||||
// For example, if a user escape a '\\*',
|
||||
// after step 3, the result will be '\\\\\\*'
|
||||
/\\\\\\(?=[$.|*+(){^])/g,
|
||||
() => ESCAPE
|
||||
],
|
||||
|
||||
[
|
||||
// '\\\\' -> '\\'
|
||||
/\\\\/g,
|
||||
() => ESCAPE
|
||||
],
|
||||
|
||||
[
|
||||
// > The range notation, e.g. [a-zA-Z],
|
||||
// > can be used to match one of the characters in a range.
|
||||
|
||||
// `\` is escaped by step 3
|
||||
/(\\)?\[([^\]/]*?)(\\*)($|\])/g,
|
||||
(match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
|
||||
// '\\[bar]' -> '\\\\[bar\\]'
|
||||
? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
|
||||
: close === ']'
|
||||
? endEscape.length % 2 === 0
|
||||
// A normal case, and it is a range notation
|
||||
// '[bar]'
|
||||
// '[bar\\\\]'
|
||||
? `[${negateRange(sanitizeRange(range))}${endEscape}]`
|
||||
// Invalid range notaton
|
||||
// '[bar\\]' -> '[bar\\\\]'
|
||||
: '[]'
|
||||
: '[]'
|
||||
],
|
||||
|
||||
// ending
|
||||
[
|
||||
// 'js' will not match 'js.'
|
||||
// 'ab' will not match 'abc'
|
||||
/(?:[^*])$/,
|
||||
|
||||
// WTF!
|
||||
// https://git-scm.com/docs/gitignore
|
||||
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
||||
// which re-fixes #24, #38
|
||||
|
||||
// > If there is a separator at the end of the pattern then the pattern
|
||||
// > will only match directories, otherwise the pattern can match both
|
||||
// > files and directories.
|
||||
|
||||
// 'js*' will not match 'a.js'
|
||||
// 'js/' will not match 'a.js'
|
||||
// 'js' will match 'a.js' and 'a.js/'
|
||||
match => /\/$/.test(match)
|
||||
// foo/ will not match 'foo'
|
||||
? `${match}$`
|
||||
// foo matches 'foo' and 'foo/'
|
||||
: `${match}(?=$|\\/$)`
|
||||
]
|
||||
]
|
||||
|
||||
const REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/
|
||||
const MODE_IGNORE = 'regex'
|
||||
const MODE_CHECK_IGNORE = 'checkRegex'
|
||||
const UNDERSCORE = '_'
|
||||
|
||||
const TRAILING_WILD_CARD_REPLACERS = {
|
||||
[MODE_IGNORE] (_, p1) {
|
||||
const prefix = p1
|
||||
// '\^':
|
||||
// '/*' does not match EMPTY
|
||||
// '/*' does not match everything
|
||||
|
||||
// '\\\/':
|
||||
// 'abc/*' does not match 'abc/'
|
||||
? `${p1}[^/]+`
|
||||
|
||||
// 'a*' matches 'a'
|
||||
// 'a*' matches 'aa'
|
||||
: '[^/]*'
|
||||
|
||||
return `${prefix}(?=$|\\/$)`
|
||||
},
|
||||
|
||||
[MODE_CHECK_IGNORE] (_, p1) {
|
||||
// When doing `git check-ignore`
|
||||
const prefix = p1
|
||||
// '\\\/':
|
||||
// 'abc/*' DOES match 'abc/' !
|
||||
? `${p1}[^/]*`
|
||||
|
||||
// 'a*' matches 'a'
|
||||
// 'a*' matches 'aa'
|
||||
: '[^/]*'
|
||||
|
||||
return `${prefix}(?=$|\\/$)`
|
||||
}
|
||||
}
|
||||
|
||||
// @param {pattern}
|
||||
const makeRegexPrefix = pattern => REPLACERS.reduce(
|
||||
(prev, [matcher, replacer]) =>
|
||||
prev.replace(matcher, replacer.bind(pattern)),
|
||||
pattern
|
||||
)
|
||||
|
||||
const isString = subject => typeof subject === 'string'
|
||||
|
||||
// > A blank line matches no files, so it can serve as a separator for readability.
|
||||
const checkPattern = pattern => pattern
|
||||
&& isString(pattern)
|
||||
&& !REGEX_TEST_BLANK_LINE.test(pattern)
|
||||
&& !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
|
||||
|
||||
// > A line starting with # serves as a comment.
|
||||
&& pattern.indexOf('#') !== 0
|
||||
|
||||
const splitPattern = pattern => pattern
|
||||
.split(REGEX_SPLITALL_CRLF)
|
||||
.filter(Boolean)
|
||||
|
||||
class IgnoreRule {
|
||||
constructor (
|
||||
pattern,
|
||||
mark,
|
||||
body,
|
||||
ignoreCase,
|
||||
negative,
|
||||
prefix
|
||||
) {
|
||||
this.pattern = pattern
|
||||
this.mark = mark
|
||||
this.negative = negative
|
||||
|
||||
define(this, 'body', body)
|
||||
define(this, 'ignoreCase', ignoreCase)
|
||||
define(this, 'regexPrefix', prefix)
|
||||
}
|
||||
|
||||
get regex () {
|
||||
const key = UNDERSCORE + MODE_IGNORE
|
||||
|
||||
if (this[key]) {
|
||||
return this[key]
|
||||
}
|
||||
|
||||
return this._make(MODE_IGNORE, key)
|
||||
}
|
||||
|
||||
get checkRegex () {
|
||||
const key = UNDERSCORE + MODE_CHECK_IGNORE
|
||||
|
||||
if (this[key]) {
|
||||
return this[key]
|
||||
}
|
||||
|
||||
return this._make(MODE_CHECK_IGNORE, key)
|
||||
}
|
||||
|
||||
_make (mode, key) {
|
||||
const str = this.regexPrefix.replace(
|
||||
REGEX_REPLACE_TRAILING_WILDCARD,
|
||||
|
||||
// It does not need to bind pattern
|
||||
TRAILING_WILD_CARD_REPLACERS[mode]
|
||||
)
|
||||
|
||||
const regex = this.ignoreCase
|
||||
? new RegExp(str, 'i')
|
||||
: new RegExp(str)
|
||||
|
||||
return define(this, key, regex)
|
||||
}
|
||||
}
|
||||
|
||||
const createRule = ({
|
||||
pattern,
|
||||
mark
|
||||
}, ignoreCase) => {
|
||||
let negative = false
|
||||
let body = pattern
|
||||
|
||||
// > An optional prefix "!" which negates the pattern;
|
||||
if (body.indexOf('!') === 0) {
|
||||
negative = true
|
||||
body = body.substr(1)
|
||||
}
|
||||
|
||||
body = body
|
||||
// > Put a backslash ("\") in front of the first "!" for patterns that
|
||||
// > begin with a literal "!", for example, `"\!important!.txt"`.
|
||||
.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
|
||||
// > Put a backslash ("\") in front of the first hash for patterns that
|
||||
// > begin with a hash.
|
||||
.replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#')
|
||||
|
||||
const regexPrefix = makeRegexPrefix(body)
|
||||
|
||||
return new IgnoreRule(
|
||||
pattern,
|
||||
mark,
|
||||
body,
|
||||
ignoreCase,
|
||||
negative,
|
||||
regexPrefix
|
||||
)
|
||||
}
|
||||
|
||||
class RuleManager {
|
||||
constructor (ignoreCase) {
|
||||
this._ignoreCase = ignoreCase
|
||||
this._rules = []
|
||||
}
|
||||
|
||||
_add (pattern) {
|
||||
// #32
|
||||
if (pattern && pattern[KEY_IGNORE]) {
|
||||
this._rules = this._rules.concat(pattern._rules._rules)
|
||||
this._added = true
|
||||
return
|
||||
}
|
||||
|
||||
if (isString(pattern)) {
|
||||
pattern = {
|
||||
pattern
|
||||
}
|
||||
}
|
||||
|
||||
if (checkPattern(pattern.pattern)) {
|
||||
const rule = createRule(pattern, this._ignoreCase)
|
||||
this._added = true
|
||||
this._rules.push(rule)
|
||||
}
|
||||
}
|
||||
|
||||
// @param {Array<string> | string | Ignore} pattern
|
||||
add (pattern) {
|
||||
this._added = false
|
||||
|
||||
makeArray(
|
||||
isString(pattern)
|
||||
? splitPattern(pattern)
|
||||
: pattern
|
||||
).forEach(this._add, this)
|
||||
|
||||
return this._added
|
||||
}
|
||||
|
||||
// Test one single path without recursively checking parent directories
|
||||
//
|
||||
// - checkUnignored `boolean` whether should check if the path is unignored,
|
||||
// setting `checkUnignored` to `false` could reduce additional
|
||||
// path matching.
|
||||
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
||||
|
||||
// @returns {TestResult} true if a file is ignored
|
||||
test (path, checkUnignored, mode) {
|
||||
let ignored = false
|
||||
let unignored = false
|
||||
let matchedRule
|
||||
|
||||
this._rules.forEach(rule => {
|
||||
const {negative} = rule
|
||||
|
||||
// | ignored : unignored
|
||||
// -------- | ---------------------------------------
|
||||
// negative | 0:0 | 0:1 | 1:0 | 1:1
|
||||
// -------- | ------- | ------- | ------- | --------
|
||||
// 0 | TEST | TEST | SKIP | X
|
||||
// 1 | TESTIF | SKIP | TEST | X
|
||||
|
||||
// - SKIP: always skip
|
||||
// - TEST: always test
|
||||
// - TESTIF: only test if checkUnignored
|
||||
// - X: that never happen
|
||||
if (
|
||||
unignored === negative && ignored !== unignored
|
||||
|| negative && !ignored && !unignored && !checkUnignored
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const matched = rule[mode].test(path)
|
||||
|
||||
if (!matched) {
|
||||
return
|
||||
}
|
||||
|
||||
ignored = !negative
|
||||
unignored = negative
|
||||
|
||||
matchedRule = negative
|
||||
? UNDEFINED
|
||||
: rule
|
||||
})
|
||||
|
||||
const ret = {
|
||||
ignored,
|
||||
unignored
|
||||
}
|
||||
|
||||
if (matchedRule) {
|
||||
ret.rule = matchedRule
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
const throwError = (message, Ctor) => {
|
||||
throw new Ctor(message)
|
||||
}
|
||||
|
||||
const checkPath = (path, originalPath, doThrow) => {
|
||||
if (!isString(path)) {
|
||||
return doThrow(
|
||||
`path must be a string, but got \`${originalPath}\``,
|
||||
TypeError
|
||||
)
|
||||
}
|
||||
|
||||
// We don't know if we should ignore EMPTY, so throw
|
||||
if (!path) {
|
||||
return doThrow(`path must not be empty`, TypeError)
|
||||
}
|
||||
|
||||
// Check if it is a relative path
|
||||
if (checkPath.isNotRelative(path)) {
|
||||
const r = '`path.relative()`d'
|
||||
return doThrow(
|
||||
`path should be a ${r} string, but got "${originalPath}"`,
|
||||
RangeError
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path)
|
||||
|
||||
checkPath.isNotRelative = isNotRelative
|
||||
|
||||
// On windows, the following function will be replaced
|
||||
/* istanbul ignore next */
|
||||
checkPath.convert = p => p
|
||||
|
||||
|
||||
class Ignore {
|
||||
constructor ({
|
||||
ignorecase = true,
|
||||
ignoreCase = ignorecase,
|
||||
allowRelativePaths = false
|
||||
} = {}) {
|
||||
define(this, KEY_IGNORE, true)
|
||||
|
||||
this._rules = new RuleManager(ignoreCase)
|
||||
this._strictPathCheck = !allowRelativePaths
|
||||
this._initCache()
|
||||
}
|
||||
|
||||
_initCache () {
|
||||
// A cache for the result of `.ignores()`
|
||||
this._ignoreCache = Object.create(null)
|
||||
|
||||
// A cache for the result of `.test()`
|
||||
this._testCache = Object.create(null)
|
||||
}
|
||||
|
||||
add (pattern) {
|
||||
if (this._rules.add(pattern)) {
|
||||
// Some rules have just added to the ignore,
|
||||
// making the behavior changed,
|
||||
// so we need to re-initialize the result cache
|
||||
this._initCache()
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
// legacy
|
||||
addPattern (pattern) {
|
||||
return this.add(pattern)
|
||||
}
|
||||
|
||||
// @returns {TestResult}
|
||||
_test (originalPath, cache, checkUnignored, slices) {
|
||||
const path = originalPath
|
||||
// Supports nullable path
|
||||
&& checkPath.convert(originalPath)
|
||||
|
||||
checkPath(
|
||||
path,
|
||||
originalPath,
|
||||
this._strictPathCheck
|
||||
? throwError
|
||||
: RETURN_FALSE
|
||||
)
|
||||
|
||||
return this._t(path, cache, checkUnignored, slices)
|
||||
}
|
||||
|
||||
checkIgnore (path) {
|
||||
// If the path doest not end with a slash, `.ignores()` is much equivalent
|
||||
// to `git check-ignore`
|
||||
if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
|
||||
return this.test(path)
|
||||
}
|
||||
|
||||
const slices = path.split(SLASH).filter(Boolean)
|
||||
slices.pop()
|
||||
|
||||
if (slices.length) {
|
||||
const parent = this._t(
|
||||
slices.join(SLASH) + SLASH,
|
||||
this._testCache,
|
||||
true,
|
||||
slices
|
||||
)
|
||||
|
||||
if (parent.ignored) {
|
||||
return parent
|
||||
}
|
||||
}
|
||||
|
||||
return this._rules.test(path, false, MODE_CHECK_IGNORE)
|
||||
}
|
||||
|
||||
_t (
|
||||
// The path to be tested
|
||||
path,
|
||||
|
||||
// The cache for the result of a certain checking
|
||||
cache,
|
||||
|
||||
// Whether should check if the path is unignored
|
||||
checkUnignored,
|
||||
|
||||
// The path slices
|
||||
slices
|
||||
) {
|
||||
if (path in cache) {
|
||||
return cache[path]
|
||||
}
|
||||
|
||||
if (!slices) {
|
||||
// path/to/a.js
|
||||
// ['path', 'to', 'a.js']
|
||||
slices = path.split(SLASH).filter(Boolean)
|
||||
}
|
||||
|
||||
slices.pop()
|
||||
|
||||
// If the path has no parent directory, just test it
|
||||
if (!slices.length) {
|
||||
return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE)
|
||||
}
|
||||
|
||||
const parent = this._t(
|
||||
slices.join(SLASH) + SLASH,
|
||||
cache,
|
||||
checkUnignored,
|
||||
slices
|
||||
)
|
||||
|
||||
// If the path contains a parent directory, check the parent first
|
||||
return cache[path] = parent.ignored
|
||||
// > It is not possible to re-include a file if a parent directory of
|
||||
// > that file is excluded.
|
||||
? parent
|
||||
: this._rules.test(path, checkUnignored, MODE_IGNORE)
|
||||
}
|
||||
|
||||
ignores (path) {
|
||||
return this._test(path, this._ignoreCache, false).ignored
|
||||
}
|
||||
|
||||
createFilter () {
|
||||
return path => !this.ignores(path)
|
||||
}
|
||||
|
||||
filter (paths) {
|
||||
return makeArray(paths).filter(this.createFilter())
|
||||
}
|
||||
|
||||
// @returns {TestResult}
|
||||
test (path) {
|
||||
return this._test(path, this._testCache, true)
|
||||
}
|
||||
}
|
||||
|
||||
const factory = options => new Ignore(options)
|
||||
|
||||
const isPathValid = path =>
|
||||
checkPath(path && checkPath.convert(path), path, RETURN_FALSE)
|
||||
|
||||
/* istanbul ignore next */
|
||||
const setupWindows = () => {
|
||||
/* eslint no-control-regex: "off" */
|
||||
const makePosix = str => /^\\\\\?\\/.test(str)
|
||||
|| /["<>|\u0000-\u001F]+/u.test(str)
|
||||
? str
|
||||
: str.replace(/\\/g, '/')
|
||||
|
||||
checkPath.convert = makePosix
|
||||
|
||||
// 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
|
||||
// 'd:\\foo'
|
||||
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i
|
||||
checkPath.isNotRelative = path =>
|
||||
REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path)
|
||||
|| isNotRelative(path)
|
||||
}
|
||||
|
||||
|
||||
// Windows
|
||||
// --------------------------------------------------------------
|
||||
/* istanbul ignore next */
|
||||
if (
|
||||
// Detect `process` so that it can run in browsers.
|
||||
typeof process !== 'undefined'
|
||||
&& process.platform === 'win32'
|
||||
) {
|
||||
setupWindows()
|
||||
}
|
||||
|
||||
// COMMONJS_EXPORTS ////////////////////////////////////////////////////////////
|
||||
|
||||
module.exports = factory
|
||||
|
||||
// Although it is an anti-pattern,
|
||||
// it is still widely misused by a lot of libraries in github
|
||||
// Ref: https://github.com/search?q=ignore.default%28%29&type=code
|
||||
factory.default = factory
|
||||
|
||||
module.exports.isPathValid = isPathValid
|
||||
|
||||
// For testing purposes
|
||||
define(module.exports, Symbol.for('setupWindows'), setupWindows)
|
||||
690
node_modules/@react-grab/cli/node_modules/ignore/legacy.js
generated
vendored
Normal file
690
node_modules/@react-grab/cli/node_modules/ignore/legacy.js
generated
vendored
Normal file
@@ -0,0 +1,690 @@
|
||||
"use strict";
|
||||
|
||||
var _TRAILING_WILD_CARD_R;
|
||||
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
||||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
||||
function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
|
||||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
|
||||
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
||||
// A simple implementation of make-array
|
||||
function makeArray(subject) {
|
||||
return Array.isArray(subject) ? subject : [subject];
|
||||
}
|
||||
var UNDEFINED = undefined;
|
||||
var EMPTY = '';
|
||||
var SPACE = ' ';
|
||||
var ESCAPE = '\\';
|
||||
var REGEX_TEST_BLANK_LINE = /^\s+$/;
|
||||
var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
||||
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
||||
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
||||
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
||||
|
||||
// Invalid:
|
||||
// - /foo,
|
||||
// - ./foo,
|
||||
// - ../foo,
|
||||
// - .
|
||||
// - ..
|
||||
// Valid:
|
||||
// - .foo
|
||||
var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
|
||||
var REGEX_TEST_TRAILING_SLASH = /\/$/;
|
||||
var SLASH = '/';
|
||||
|
||||
// Do not use ternary expression here, since "istanbul ignore next" is buggy
|
||||
var TMP_KEY_IGNORE = 'node-ignore';
|
||||
/* istanbul ignore else */
|
||||
if (typeof Symbol !== 'undefined') {
|
||||
TMP_KEY_IGNORE = Symbol["for"]('node-ignore');
|
||||
}
|
||||
var KEY_IGNORE = TMP_KEY_IGNORE;
|
||||
var define = function define(object, key, value) {
|
||||
Object.defineProperty(object, key, {
|
||||
value: value
|
||||
});
|
||||
return value;
|
||||
};
|
||||
var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
|
||||
var RETURN_FALSE = function RETURN_FALSE() {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Sanitize the range of a regular expression
|
||||
// The cases are complicated, see test cases for details
|
||||
var sanitizeRange = function sanitizeRange(range) {
|
||||
return range.replace(REGEX_REGEXP_RANGE, function (match, from, to) {
|
||||
return from.charCodeAt(0) <= to.charCodeAt(0) ? match
|
||||
// Invalid range (out of order) which is ok for gitignore rules but
|
||||
// fatal for JavaScript regular expression, so eliminate it.
|
||||
: EMPTY;
|
||||
});
|
||||
};
|
||||
|
||||
// > An optional `!` or `^` at the start of a class negates it, so that it
|
||||
// > matches any character not in the set. (gitignore(5), fnmatch(3))
|
||||
// The leading `^` has already been escaped to `\^` by the metacharacter
|
||||
// escaper, so we strip the literal `!` or escaped `^` and emit a single
|
||||
// regex `^` which is the JavaScript negation token.
|
||||
var negateRange = function negateRange(range) {
|
||||
return range.startsWith('!') || range.startsWith('\\^') ? "^".concat(range.slice(range[0] === '!' ? 1 : 2)) : range;
|
||||
};
|
||||
|
||||
// See fixtures #59
|
||||
var cleanRangeBackSlash = function cleanRangeBackSlash(slashes) {
|
||||
var length = slashes.length;
|
||||
return slashes.slice(0, length - length % 2);
|
||||
};
|
||||
|
||||
// > If the pattern ends with a slash,
|
||||
// > it is removed for the purpose of the following description,
|
||||
// > but it would only find a match with a directory.
|
||||
// > In other words, foo/ will match a directory foo and paths underneath it,
|
||||
// > but will not match a regular file or a symbolic link foo
|
||||
// > (this is consistent with the way how pathspec works in general in Git).
|
||||
// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`'
|
||||
// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call
|
||||
// you could use option `mark: true` with `glob`
|
||||
|
||||
// '`foo/`' should not continue with the '`..`'
|
||||
var REPLACERS = [[
|
||||
// Remove BOM
|
||||
// TODO:
|
||||
// Other similar zero-width characters?
|
||||
/^\uFEFF/, function () {
|
||||
return EMPTY;
|
||||
}],
|
||||
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
||||
[
|
||||
// (a\ ) -> (a )
|
||||
// (a ) -> (a)
|
||||
// (a ) -> (a)
|
||||
// (a \ ) -> (a )
|
||||
/((?:\\\\)*?)(\\?\s+)$/, function (_, m1, m2) {
|
||||
return m1 + (m2.indexOf('\\') === 0 ? SPACE : EMPTY);
|
||||
}],
|
||||
// Replace (\ ) with ' '
|
||||
// (\ ) -> ' '
|
||||
// (\\ ) -> '\\ '
|
||||
// (\\\ ) -> '\\ '
|
||||
[/(\\+?)\s/g, function (_, m1) {
|
||||
var length = m1.length;
|
||||
return m1.slice(0, length - length % 2) + SPACE;
|
||||
}],
|
||||
// Escape metacharacters
|
||||
// which is written down by users but means special for regular expressions.
|
||||
|
||||
// > There are 12 characters with special meanings:
|
||||
// > - the backslash \,
|
||||
// > - the caret ^,
|
||||
// > - the dollar sign $,
|
||||
// > - the period or dot .,
|
||||
// > - the vertical bar or pipe symbol |,
|
||||
// > - the question mark ?,
|
||||
// > - the asterisk or star *,
|
||||
// > - the plus sign +,
|
||||
// > - the opening parenthesis (,
|
||||
// > - the closing parenthesis ),
|
||||
// > - and the opening square bracket [,
|
||||
// > - the opening curly brace {,
|
||||
// > These special characters are often called "metacharacters".
|
||||
[/[\\$.|*+(){^]/g, function (match) {
|
||||
return "\\".concat(match);
|
||||
}], [
|
||||
// > a question mark (?) matches a single character
|
||||
/(?!\\)\?/g, function () {
|
||||
return '[^/]';
|
||||
}],
|
||||
// leading slash
|
||||
[
|
||||
// > A leading slash matches the beginning of the pathname.
|
||||
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
||||
// A leading slash matches the beginning of the pathname
|
||||
/^\//, function () {
|
||||
return '^';
|
||||
}],
|
||||
// replace special metacharacter slash after the leading slash
|
||||
[/\//g, function () {
|
||||
return '\\/';
|
||||
}], [
|
||||
// > A leading "**" followed by a slash means match in all directories.
|
||||
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
||||
// > the same as pattern "foo".
|
||||
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
||||
// > under directory "foo".
|
||||
// Notice that the '*'s have been replaced as '\\*'
|
||||
/^\^*(?:\\\*\\\*\\\/)+/,
|
||||
// '**/foo' <-> 'foo'
|
||||
function () {
|
||||
return '^(?:.*\\/)?';
|
||||
}],
|
||||
// starting
|
||||
[
|
||||
// there will be no leading '/'
|
||||
// (which has been replaced by section "leading slash")
|
||||
// If starts with '**', adding a '^' to the regular expression also works
|
||||
/^(?=[^^])/, function startingReplacer() {
|
||||
// If has a slash `/` at the beginning or middle
|
||||
return !/\/(?!$)/.test(this)
|
||||
// > Prior to 2.22.1
|
||||
// > If the pattern does not contain a slash /,
|
||||
// > Git treats it as a shell glob pattern
|
||||
// Actually, if there is only a trailing slash,
|
||||
// git also treats it as a shell glob pattern
|
||||
|
||||
// After 2.22.1 (compatible but clearer)
|
||||
// > If there is a separator at the beginning or middle (or both)
|
||||
// > of the pattern, then the pattern is relative to the directory
|
||||
// > level of the particular .gitignore file itself.
|
||||
// > Otherwise the pattern may also match at any level below
|
||||
// > the .gitignore level.
|
||||
? '(?:^|\\/)'
|
||||
|
||||
// > Otherwise, Git treats the pattern as a shell glob suitable for
|
||||
// > consumption by fnmatch(3)
|
||||
: '^';
|
||||
}],
|
||||
// two globstars
|
||||
[
|
||||
// Use lookahead assertions so that we could match more than one `'/**'`
|
||||
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
||||
// Zero, one or several directories
|
||||
// should not use '*', or it will be replaced by the next replacer
|
||||
|
||||
// Check if it is not the last `'/**'`
|
||||
function (_, index, str) {
|
||||
return index + 6 < str.length
|
||||
|
||||
// case: /**/
|
||||
// > A slash followed by two consecutive asterisks then a slash matches
|
||||
// > zero or more directories.
|
||||
// > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
|
||||
// '/**/'
|
||||
? '(?:\\/[^\\/]+)*'
|
||||
|
||||
// case: /**
|
||||
// > A trailing `"/**"` matches everything inside.
|
||||
|
||||
// #21: everything inside but it should not include the current folder
|
||||
: '\\/.+';
|
||||
}],
|
||||
// normal intermediate wildcards
|
||||
[
|
||||
// Never replace escaped '*'
|
||||
// ignore rule '\*' will match the path '*'
|
||||
|
||||
// 'abc.*/' -> go
|
||||
// 'abc.*' -> skip this rule,
|
||||
// coz trailing single wildcard will be handed by [trailing wildcard]
|
||||
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
||||
// '*.js' matches '.js'
|
||||
// '*.js' doesn't match 'abc'
|
||||
function (_, p1, p2) {
|
||||
// 1.
|
||||
// > An asterisk "*" matches anything except a slash.
|
||||
// 2.
|
||||
// > Other consecutive asterisks are considered regular asterisks
|
||||
// > and will match according to the previous rules.
|
||||
var unescaped = p2.replace(/\\\*/g, '[^\\/]*');
|
||||
return p1 + unescaped;
|
||||
}], [
|
||||
// unescape, revert step 3 except for back slash
|
||||
// For example, if a user escape a '\\*',
|
||||
// after step 3, the result will be '\\\\\\*'
|
||||
/\\\\\\(?=[$.|*+(){^])/g, function () {
|
||||
return ESCAPE;
|
||||
}], [
|
||||
// '\\\\' -> '\\'
|
||||
/\\\\/g, function () {
|
||||
return ESCAPE;
|
||||
}], [
|
||||
// > The range notation, e.g. [a-zA-Z],
|
||||
// > can be used to match one of the characters in a range.
|
||||
|
||||
// `\` is escaped by step 3
|
||||
/(\\)?\[([^\]/]*?)(\\*)($|\])/g, function (match, leadEscape, range, endEscape, close) {
|
||||
return leadEscape === ESCAPE
|
||||
// '\\[bar]' -> '\\\\[bar\\]'
|
||||
? "\\[".concat(range).concat(cleanRangeBackSlash(endEscape)).concat(close) : close === ']' ? endEscape.length % 2 === 0
|
||||
// A normal case, and it is a range notation
|
||||
// '[bar]'
|
||||
// '[bar\\\\]'
|
||||
? "[".concat(negateRange(sanitizeRange(range))).concat(endEscape, "]") // Invalid range notaton
|
||||
// '[bar\\]' -> '[bar\\\\]'
|
||||
: '[]' : '[]';
|
||||
}],
|
||||
// ending
|
||||
[
|
||||
// 'js' will not match 'js.'
|
||||
// 'ab' will not match 'abc'
|
||||
/(?:[^*])$/,
|
||||
// WTF!
|
||||
// https://git-scm.com/docs/gitignore
|
||||
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
||||
// which re-fixes #24, #38
|
||||
|
||||
// > If there is a separator at the end of the pattern then the pattern
|
||||
// > will only match directories, otherwise the pattern can match both
|
||||
// > files and directories.
|
||||
|
||||
// 'js*' will not match 'a.js'
|
||||
// 'js/' will not match 'a.js'
|
||||
// 'js' will match 'a.js' and 'a.js/'
|
||||
function (match) {
|
||||
return /\/$/.test(match)
|
||||
// foo/ will not match 'foo'
|
||||
? "".concat(match, "$") // foo matches 'foo' and 'foo/'
|
||||
: "".concat(match, "(?=$|\\/$)");
|
||||
}]];
|
||||
var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
|
||||
var MODE_IGNORE = 'regex';
|
||||
var MODE_CHECK_IGNORE = 'checkRegex';
|
||||
var UNDERSCORE = '_';
|
||||
var TRAILING_WILD_CARD_REPLACERS = (_TRAILING_WILD_CARD_R = {}, _defineProperty(_TRAILING_WILD_CARD_R, MODE_IGNORE, function (_, p1) {
|
||||
var prefix = p1
|
||||
// '\^':
|
||||
// '/*' does not match EMPTY
|
||||
// '/*' does not match everything
|
||||
|
||||
// '\\\/':
|
||||
// 'abc/*' does not match 'abc/'
|
||||
? "".concat(p1, "[^/]+") // 'a*' matches 'a'
|
||||
// 'a*' matches 'aa'
|
||||
: '[^/]*';
|
||||
return "".concat(prefix, "(?=$|\\/$)");
|
||||
}), _defineProperty(_TRAILING_WILD_CARD_R, MODE_CHECK_IGNORE, function (_, p1) {
|
||||
// When doing `git check-ignore`
|
||||
var prefix = p1
|
||||
// '\\\/':
|
||||
// 'abc/*' DOES match 'abc/' !
|
||||
? "".concat(p1, "[^/]*") // 'a*' matches 'a'
|
||||
// 'a*' matches 'aa'
|
||||
: '[^/]*';
|
||||
return "".concat(prefix, "(?=$|\\/$)");
|
||||
}), _TRAILING_WILD_CARD_R);
|
||||
|
||||
// @param {pattern}
|
||||
var makeRegexPrefix = function makeRegexPrefix(pattern) {
|
||||
return REPLACERS.reduce(function (prev, _ref) {
|
||||
var _ref2 = _slicedToArray(_ref, 2),
|
||||
matcher = _ref2[0],
|
||||
replacer = _ref2[1];
|
||||
return prev.replace(matcher, replacer.bind(pattern));
|
||||
}, pattern);
|
||||
};
|
||||
var isString = function isString(subject) {
|
||||
return typeof subject === 'string';
|
||||
};
|
||||
|
||||
// > A blank line matches no files, so it can serve as a separator for readability.
|
||||
var checkPattern = function checkPattern(pattern) {
|
||||
return pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
|
||||
|
||||
// > A line starting with # serves as a comment.
|
||||
&& pattern.indexOf('#') !== 0;
|
||||
};
|
||||
var splitPattern = function splitPattern(pattern) {
|
||||
return pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
|
||||
};
|
||||
var IgnoreRule = /*#__PURE__*/function () {
|
||||
function IgnoreRule(pattern, mark, body, ignoreCase, negative, prefix) {
|
||||
_classCallCheck(this, IgnoreRule);
|
||||
this.pattern = pattern;
|
||||
this.mark = mark;
|
||||
this.negative = negative;
|
||||
define(this, 'body', body);
|
||||
define(this, 'ignoreCase', ignoreCase);
|
||||
define(this, 'regexPrefix', prefix);
|
||||
}
|
||||
_createClass(IgnoreRule, [{
|
||||
key: "regex",
|
||||
get: function get() {
|
||||
var key = UNDERSCORE + MODE_IGNORE;
|
||||
if (this[key]) {
|
||||
return this[key];
|
||||
}
|
||||
return this._make(MODE_IGNORE, key);
|
||||
}
|
||||
}, {
|
||||
key: "checkRegex",
|
||||
get: function get() {
|
||||
var key = UNDERSCORE + MODE_CHECK_IGNORE;
|
||||
if (this[key]) {
|
||||
return this[key];
|
||||
}
|
||||
return this._make(MODE_CHECK_IGNORE, key);
|
||||
}
|
||||
}, {
|
||||
key: "_make",
|
||||
value: function _make(mode, key) {
|
||||
var str = this.regexPrefix.replace(REGEX_REPLACE_TRAILING_WILDCARD,
|
||||
// It does not need to bind pattern
|
||||
TRAILING_WILD_CARD_REPLACERS[mode]);
|
||||
var regex = this.ignoreCase ? new RegExp(str, 'i') : new RegExp(str);
|
||||
return define(this, key, regex);
|
||||
}
|
||||
}]);
|
||||
return IgnoreRule;
|
||||
}();
|
||||
var createRule = function createRule(_ref3, ignoreCase) {
|
||||
var pattern = _ref3.pattern,
|
||||
mark = _ref3.mark;
|
||||
var negative = false;
|
||||
var body = pattern;
|
||||
|
||||
// > An optional prefix "!" which negates the pattern;
|
||||
if (body.indexOf('!') === 0) {
|
||||
negative = true;
|
||||
body = body.substr(1);
|
||||
}
|
||||
body = body
|
||||
// > Put a backslash ("\") in front of the first "!" for patterns that
|
||||
// > begin with a literal "!", for example, `"\!important!.txt"`.
|
||||
.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!')
|
||||
// > Put a backslash ("\") in front of the first hash for patterns that
|
||||
// > begin with a hash.
|
||||
.replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#');
|
||||
var regexPrefix = makeRegexPrefix(body);
|
||||
return new IgnoreRule(pattern, mark, body, ignoreCase, negative, regexPrefix);
|
||||
};
|
||||
var RuleManager = /*#__PURE__*/function () {
|
||||
function RuleManager(ignoreCase) {
|
||||
_classCallCheck(this, RuleManager);
|
||||
this._ignoreCase = ignoreCase;
|
||||
this._rules = [];
|
||||
}
|
||||
_createClass(RuleManager, [{
|
||||
key: "_add",
|
||||
value: function _add(pattern) {
|
||||
// #32
|
||||
if (pattern && pattern[KEY_IGNORE]) {
|
||||
this._rules = this._rules.concat(pattern._rules._rules);
|
||||
this._added = true;
|
||||
return;
|
||||
}
|
||||
if (isString(pattern)) {
|
||||
pattern = {
|
||||
pattern: pattern
|
||||
};
|
||||
}
|
||||
if (checkPattern(pattern.pattern)) {
|
||||
var rule = createRule(pattern, this._ignoreCase);
|
||||
this._added = true;
|
||||
this._rules.push(rule);
|
||||
}
|
||||
}
|
||||
|
||||
// @param {Array<string> | string | Ignore} pattern
|
||||
}, {
|
||||
key: "add",
|
||||
value: function add(pattern) {
|
||||
this._added = false;
|
||||
makeArray(isString(pattern) ? splitPattern(pattern) : pattern).forEach(this._add, this);
|
||||
return this._added;
|
||||
}
|
||||
|
||||
// Test one single path without recursively checking parent directories
|
||||
//
|
||||
// - checkUnignored `boolean` whether should check if the path is unignored,
|
||||
// setting `checkUnignored` to `false` could reduce additional
|
||||
// path matching.
|
||||
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
||||
|
||||
// @returns {TestResult} true if a file is ignored
|
||||
}, {
|
||||
key: "test",
|
||||
value: function test(path, checkUnignored, mode) {
|
||||
var ignored = false;
|
||||
var unignored = false;
|
||||
var matchedRule;
|
||||
this._rules.forEach(function (rule) {
|
||||
var negative = rule.negative;
|
||||
|
||||
// | ignored : unignored
|
||||
// -------- | ---------------------------------------
|
||||
// negative | 0:0 | 0:1 | 1:0 | 1:1
|
||||
// -------- | ------- | ------- | ------- | --------
|
||||
// 0 | TEST | TEST | SKIP | X
|
||||
// 1 | TESTIF | SKIP | TEST | X
|
||||
|
||||
// - SKIP: always skip
|
||||
// - TEST: always test
|
||||
// - TESTIF: only test if checkUnignored
|
||||
// - X: that never happen
|
||||
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
||||
return;
|
||||
}
|
||||
var matched = rule[mode].test(path);
|
||||
if (!matched) {
|
||||
return;
|
||||
}
|
||||
ignored = !negative;
|
||||
unignored = negative;
|
||||
matchedRule = negative ? UNDEFINED : rule;
|
||||
});
|
||||
var ret = {
|
||||
ignored: ignored,
|
||||
unignored: unignored
|
||||
};
|
||||
if (matchedRule) {
|
||||
ret.rule = matchedRule;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}]);
|
||||
return RuleManager;
|
||||
}();
|
||||
var throwError = function throwError(message, Ctor) {
|
||||
throw new Ctor(message);
|
||||
};
|
||||
var checkPath = function checkPath(path, originalPath, doThrow) {
|
||||
if (!isString(path)) {
|
||||
return doThrow("path must be a string, but got `".concat(originalPath, "`"), TypeError);
|
||||
}
|
||||
|
||||
// We don't know if we should ignore EMPTY, so throw
|
||||
if (!path) {
|
||||
return doThrow("path must not be empty", TypeError);
|
||||
}
|
||||
|
||||
// Check if it is a relative path
|
||||
if (checkPath.isNotRelative(path)) {
|
||||
var r = '`path.relative()`d';
|
||||
return doThrow("path should be a ".concat(r, " string, but got \"").concat(originalPath, "\""), RangeError);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
var isNotRelative = function isNotRelative(path) {
|
||||
return REGEX_TEST_INVALID_PATH.test(path);
|
||||
};
|
||||
checkPath.isNotRelative = isNotRelative;
|
||||
|
||||
// On windows, the following function will be replaced
|
||||
/* istanbul ignore next */
|
||||
checkPath.convert = function (p) {
|
||||
return p;
|
||||
};
|
||||
var Ignore = /*#__PURE__*/function () {
|
||||
function Ignore() {
|
||||
var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
||||
_ref4$ignorecase = _ref4.ignorecase,
|
||||
ignorecase = _ref4$ignorecase === void 0 ? true : _ref4$ignorecase,
|
||||
_ref4$ignoreCase = _ref4.ignoreCase,
|
||||
ignoreCase = _ref4$ignoreCase === void 0 ? ignorecase : _ref4$ignoreCase,
|
||||
_ref4$allowRelativePa = _ref4.allowRelativePaths,
|
||||
allowRelativePaths = _ref4$allowRelativePa === void 0 ? false : _ref4$allowRelativePa;
|
||||
_classCallCheck(this, Ignore);
|
||||
define(this, KEY_IGNORE, true);
|
||||
this._rules = new RuleManager(ignoreCase);
|
||||
this._strictPathCheck = !allowRelativePaths;
|
||||
this._initCache();
|
||||
}
|
||||
_createClass(Ignore, [{
|
||||
key: "_initCache",
|
||||
value: function _initCache() {
|
||||
// A cache for the result of `.ignores()`
|
||||
this._ignoreCache = Object.create(null);
|
||||
|
||||
// A cache for the result of `.test()`
|
||||
this._testCache = Object.create(null);
|
||||
}
|
||||
}, {
|
||||
key: "add",
|
||||
value: function add(pattern) {
|
||||
if (this._rules.add(pattern)) {
|
||||
// Some rules have just added to the ignore,
|
||||
// making the behavior changed,
|
||||
// so we need to re-initialize the result cache
|
||||
this._initCache();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// legacy
|
||||
}, {
|
||||
key: "addPattern",
|
||||
value: function addPattern(pattern) {
|
||||
return this.add(pattern);
|
||||
}
|
||||
|
||||
// @returns {TestResult}
|
||||
}, {
|
||||
key: "_test",
|
||||
value: function _test(originalPath, cache, checkUnignored, slices) {
|
||||
var path = originalPath
|
||||
// Supports nullable path
|
||||
&& checkPath.convert(originalPath);
|
||||
checkPath(path, originalPath, this._strictPathCheck ? throwError : RETURN_FALSE);
|
||||
return this._t(path, cache, checkUnignored, slices);
|
||||
}
|
||||
}, {
|
||||
key: "checkIgnore",
|
||||
value: function checkIgnore(path) {
|
||||
// If the path doest not end with a slash, `.ignores()` is much equivalent
|
||||
// to `git check-ignore`
|
||||
if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
|
||||
return this.test(path);
|
||||
}
|
||||
var slices = path.split(SLASH).filter(Boolean);
|
||||
slices.pop();
|
||||
if (slices.length) {
|
||||
var parent = this._t(slices.join(SLASH) + SLASH, this._testCache, true, slices);
|
||||
if (parent.ignored) {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
return this._rules.test(path, false, MODE_CHECK_IGNORE);
|
||||
}
|
||||
}, {
|
||||
key: "_t",
|
||||
value: function _t(
|
||||
// The path to be tested
|
||||
path,
|
||||
// The cache for the result of a certain checking
|
||||
cache,
|
||||
// Whether should check if the path is unignored
|
||||
checkUnignored,
|
||||
// The path slices
|
||||
slices) {
|
||||
if (path in cache) {
|
||||
return cache[path];
|
||||
}
|
||||
if (!slices) {
|
||||
// path/to/a.js
|
||||
// ['path', 'to', 'a.js']
|
||||
slices = path.split(SLASH).filter(Boolean);
|
||||
}
|
||||
slices.pop();
|
||||
|
||||
// If the path has no parent directory, just test it
|
||||
if (!slices.length) {
|
||||
return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE);
|
||||
}
|
||||
var parent = this._t(slices.join(SLASH) + SLASH, cache, checkUnignored, slices);
|
||||
|
||||
// If the path contains a parent directory, check the parent first
|
||||
return cache[path] = parent.ignored
|
||||
// > It is not possible to re-include a file if a parent directory of
|
||||
// > that file is excluded.
|
||||
? parent : this._rules.test(path, checkUnignored, MODE_IGNORE);
|
||||
}
|
||||
}, {
|
||||
key: "ignores",
|
||||
value: function ignores(path) {
|
||||
return this._test(path, this._ignoreCache, false).ignored;
|
||||
}
|
||||
}, {
|
||||
key: "createFilter",
|
||||
value: function createFilter() {
|
||||
var _this = this;
|
||||
return function (path) {
|
||||
return !_this.ignores(path);
|
||||
};
|
||||
}
|
||||
}, {
|
||||
key: "filter",
|
||||
value: function filter(paths) {
|
||||
return makeArray(paths).filter(this.createFilter());
|
||||
}
|
||||
|
||||
// @returns {TestResult}
|
||||
}, {
|
||||
key: "test",
|
||||
value: function test(path) {
|
||||
return this._test(path, this._testCache, true);
|
||||
}
|
||||
}]);
|
||||
return Ignore;
|
||||
}();
|
||||
var factory = function factory(options) {
|
||||
return new Ignore(options);
|
||||
};
|
||||
var isPathValid = function isPathValid(path) {
|
||||
return checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
var setupWindows = function setupWindows() {
|
||||
/* eslint no-control-regex: "off" */
|
||||
var makePosix = function makePosix(str) {
|
||||
return /^\\\\\?\\/.test(str) || /[\0-\x1F"<>\|]+/.test(str) ? str : str.replace(/\\/g, '/');
|
||||
};
|
||||
checkPath.convert = makePosix;
|
||||
|
||||
// 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/'
|
||||
// 'd:\\foo'
|
||||
var REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
||||
checkPath.isNotRelative = function (path) {
|
||||
return REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path) || isNotRelative(path);
|
||||
};
|
||||
};
|
||||
|
||||
// Windows
|
||||
// --------------------------------------------------------------
|
||||
/* istanbul ignore next */
|
||||
if (
|
||||
// Detect `process` so that it can run in browsers.
|
||||
typeof process !== 'undefined' && process.platform === 'win32') {
|
||||
setupWindows();
|
||||
}
|
||||
|
||||
// COMMONJS_EXPORTS ////////////////////////////////////////////////////////////
|
||||
|
||||
module.exports = factory;
|
||||
|
||||
// Although it is an anti-pattern,
|
||||
// it is still widely misused by a lot of libraries in github
|
||||
// Ref: https://github.com/search?q=ignore.default%28%29&type=code
|
||||
factory["default"] = factory;
|
||||
module.exports.isPathValid = isPathValid;
|
||||
|
||||
// For testing purposes
|
||||
define(module.exports, Symbol["for"]('setupWindows'), setupWindows);
|
||||
87
node_modules/@react-grab/cli/node_modules/ignore/package.json
generated
vendored
Normal file
87
node_modules/@react-grab/cli/node_modules/ignore/package.json
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "ignore",
|
||||
"version": "7.0.6",
|
||||
"description": "Ignore is a manager and filter for .gitignore rules, the one used by eslint, gitbook and many others.",
|
||||
"types": "index.d.ts",
|
||||
"files": [
|
||||
"legacy.js",
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"LICENSE-MIT"
|
||||
],
|
||||
"scripts": {
|
||||
"prepublishOnly": "npm run build",
|
||||
"build": "babel -o legacy.js index.js",
|
||||
|
||||
"==================== linting ======================": "",
|
||||
"lint": "eslint .",
|
||||
|
||||
"===================== import ======================": "",
|
||||
"ts": "npm run test:ts && npm run test:16",
|
||||
"test:ts": "ts-node ./test/import/simple.ts",
|
||||
"test:16": "npm run test:ts:16 && npm run test:cjs:16 && npm run test:mjs:16",
|
||||
"test:ts:16": "ts-node --compilerOptions '{\"moduleResolution\": \"Node16\", \"module\": \"Node16\"}' ./test/import/simple.ts && tsc ./test/import/simple.ts --lib ES6 --moduleResolution Node16 --module Node16 && node ./test/import/simple.js",
|
||||
"test:cjs:16": "ts-node --compilerOptions '{\"moduleResolution\": \"Node16\", \"module\": \"Node16\"}' ./test/import/simple.cjs",
|
||||
"test:mjs:16": "ts-node --compilerOptions '{\"moduleResolution\": \"Node16\", \"module\": \"Node16\"}' ./test/import/simple.mjs && babel -o ./test/import/simple-mjs.js ./test/import/simple.mjs && node ./test/import/simple-mjs.js",
|
||||
|
||||
"===================== cases =======================": "",
|
||||
"test:cases": "npm run tap test/*.test.js -- --coverage",
|
||||
"tap": "tap --reporter classic",
|
||||
|
||||
"===================== debug =======================": "",
|
||||
"test:git": "npm run tap test/git-check-ignore.test.js",
|
||||
"test:ignore": "npm run tap test/ignore.test.js",
|
||||
"test:ignore:only": "IGNORE_ONLY_IGNORES=1 npm run tap test/ignore.test.js",
|
||||
"test:others": "npm run tap test/others.test.js",
|
||||
"test:no-coverage": "npm run tap test/*.test.js -- --no-check-coverage",
|
||||
|
||||
"test": "npm run lint && npm run ts && npm run build && npm run test:cases",
|
||||
"test:win32": "IGNORE_TEST_WIN32=1 npm run test",
|
||||
"report": "tap --coverage-report=html"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:kaelzhang/node-ignore.git"
|
||||
},
|
||||
"keywords": [
|
||||
"ignore",
|
||||
".gitignore",
|
||||
"gitignore",
|
||||
"npmignore",
|
||||
"rules",
|
||||
"manager",
|
||||
"filter",
|
||||
"regexp",
|
||||
"regex",
|
||||
"fnmatch",
|
||||
"glob",
|
||||
"asterisks",
|
||||
"regular-expression"
|
||||
],
|
||||
"author": "kael",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/kaelzhang/node-ignore/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.22.9",
|
||||
"@babel/core": "^7.22.9",
|
||||
"@babel/preset-env": "^7.22.9",
|
||||
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
||||
"debug": "^4.3.4",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-config-ostai": "^3.0.0",
|
||||
"eslint-plugin-import": "^2.28.0",
|
||||
"mkdirp": "^3.0.1",
|
||||
"pre-suf": "^1.1.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"spawn-sync": "^2.0.0",
|
||||
"tap": "^16.3.9",
|
||||
"tmp": "0.2.3",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
}
|
||||
54
node_modules/@react-grab/cli/package.json
generated
vendored
Normal file
54
node_modules/@react-grab/cli/package.json
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "@react-grab/cli",
|
||||
"version": "0.1.50",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/aidenybai/react-grab.git"
|
||||
},
|
||||
"bin": {
|
||||
"react-grab": "./bin/cli.js"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"dist",
|
||||
"skills"
|
||||
],
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/cli.d.ts",
|
||||
"import": "./dist/cli.js",
|
||||
"require": "./dist/cli.cjs"
|
||||
},
|
||||
"./api": {
|
||||
"types": "./dist/api.d.ts",
|
||||
"import": "./dist/api.js",
|
||||
"require": "./dist/api.cjs"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"agent-install": "^0.0.6",
|
||||
"commander": "^14.0.3",
|
||||
"ignore": "^7.0.5",
|
||||
"ora": "^9.4.0",
|
||||
"package-manager-detector": "^1.6.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"prompts": "^2.4.2",
|
||||
"tinyexec": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/prompts": "^2.4.9",
|
||||
"cross-env": "^10.1.0",
|
||||
"vite-plus": "^0.1.20"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "node scripts/bundle-skill.mjs --watch",
|
||||
"build": "rm -rf dist skills && node scripts/bundle-skill.mjs && cross-env NODE_ENV=production vp pack && node scripts/copy-native-readers.mjs",
|
||||
"test": "vp test run",
|
||||
"test:watch": "vp test",
|
||||
"lint": "vp lint",
|
||||
"format": "vp fmt",
|
||||
"format:check": "vp fmt --check",
|
||||
"check": "vp check"
|
||||
}
|
||||
}
|
||||
82
node_modules/@react-grab/cli/skills/react-grab/SKILL.md
generated
vendored
Normal file
82
node_modules/@react-grab/cli/skills/react-grab/SKILL.md
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: react-grab
|
||||
description: >-
|
||||
Use when the user wants a hands-free loop where grabbing UI elements in the
|
||||
browser with React Grab feeds tasks to the agent automatically, with no
|
||||
copy-paste or manual handoff. Triggers: "watch react grab", "monitor my
|
||||
grabs", "auto-process react grab", "watch my clipboard for grabs". Not for a
|
||||
one-off paste of a single grab; this is the continuous, always-on loop.
|
||||
---
|
||||
|
||||
# React Grab
|
||||
|
||||
The user selects UI elements in their browser and copies them with React Grab.
|
||||
`npx react-grab@latest pull` waits for new grabs and prints each as one line of
|
||||
JSON (usually one, sometimes a few if several were copied), starting the
|
||||
background watcher automatically the first time. Run it in a loop.
|
||||
|
||||
## The loop
|
||||
|
||||
Repeat until the user says stop:
|
||||
|
||||
1. Wait for the next grab:
|
||||
|
||||
```bash
|
||||
npx react-grab@latest pull --max-age 0
|
||||
```
|
||||
|
||||
It blocks until the user grabs something, then prints the new grab(s) — one JSON
|
||||
object per line. `--max-age 0` is important: it delivers every grab regardless of
|
||||
age, so a comment the user added while you were busy on the previous task isn't
|
||||
silently dropped (the default skips grabs older than ~5 min as stale). Act on
|
||||
every line. If your shell cancels the command before a grab arrives, just run it
|
||||
again — nothing is lost; the watcher keeps capturing in the background and `pull`
|
||||
resumes where it left off.
|
||||
|
||||
2. Act on the grab (below).
|
||||
3. Go back to step 1.
|
||||
|
||||
## A new grab while you're working wins
|
||||
|
||||
The watcher never stops capturing — including while you're mid-task. A grab the
|
||||
user makes before you finish is them redirecting you, so it supersedes whatever
|
||||
you're doing. Don't make them wait for the old task to finish.
|
||||
|
||||
While acting on a grab:
|
||||
|
||||
- Run anything slow (dev servers, builds, installs, test runs) as a background
|
||||
process, never a blocking foreground call, so you stay free to notice new grabs.
|
||||
- Between steps, peek without blocking:
|
||||
|
||||
```bash
|
||||
npx react-grab@latest pull --max-age 0 --wait 0
|
||||
```
|
||||
|
||||
Empty output means nothing new — keep going. If it prints a grab, the user has
|
||||
moved on: stop the current task, cancel any background processes you started for
|
||||
it, and act on the newest grab instead.
|
||||
|
||||
## Acting on a grab
|
||||
|
||||
Each grab JSON has `content` (the element's source references) and, in prompt
|
||||
mode, `prompt` (the user's typed instruction):
|
||||
|
||||
- **`prompt` present** → that comment IS the task. Execute it against the grabbed
|
||||
source; `content` holds the references (`// path:line`, `in Component (at …)`),
|
||||
so jump straight to that file.
|
||||
- **No `prompt`** → apply the standing instruction the user set when starting the
|
||||
loop, or, if there is none, triage it (summarize component + `file:line`) and
|
||||
wait for direction.
|
||||
|
||||
## Stopping
|
||||
|
||||
When the user says stop, run this and don't pull again:
|
||||
|
||||
```bash
|
||||
npx react-grab@latest stop
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The watcher reads the clipboard on the machine it runs on — run it on the same
|
||||
machine as the browser, not over SSH or in a remote container.
|
||||
Reference in New Issue
Block a user