Skip to content

Instantly share code, notes, and snippets.

@zpao
Last active May 26, 2022 19:22
Show Gist options
  • Save zpao/7bbb58f2d44b553cf37581d338c826a4 to your computer and use it in GitHub Desktop.
Save zpao/7bbb58f2d44b553cf37581d338c826a4 to your computer and use it in GitHub Desktop.
diff --git a/packages/create-docusaurus/package.json b/packages/create-docusaurus/package.json
index 8c715fc339..5fba51f11b 100755
--- a/packages/create-docusaurus/package.json
+++ b/packages/create-docusaurus/package.json
@@ -24,11 +24,11 @@
"dependencies": {
"@docusaurus/logger": "2.0.0-beta.20",
"commander": "^5.1.0",
+ "execa": "^6.1.0",
"fs-extra": "^10.1.0",
"lodash": "^4.17.21",
"prompts": "^2.4.2",
"semver": "^7.3.7",
- "shelljs": "^0.8.5",
"supports-color": "^9.2.2",
"tslib": "^2.4.0"
},
diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts
index b35e653b0a..99f31126bd 100755
--- a/packages/create-docusaurus/src/index.ts
+++ b/packages/create-docusaurus/src/index.ts
@@ -10,7 +10,7 @@ import fs from 'fs-extra';
import {fileURLToPath} from 'url';
import prompts, {type Choice} from 'prompts';
import path from 'path';
-import shell from 'shelljs';
+import {execa} from 'execa';
import _ from 'lodash';
import supportsColor from 'supports-color';
@@ -35,6 +35,24 @@ type PackageManager = keyof typeof lockfileNames;
const packageManagers = Object.keys(lockfileNames) as PackageManager[];
+async function hasYarnInPath(): Promise<boolean> {
+ try {
+ const {exitCode} = await execa('yarn', ['--version']);
+ return exitCode === 0;
+ } catch {
+ return false;
+ }
+}
+
+async function hasPnpmInPath(): Promise<boolean> {
+ try {
+ const {exitCode} = await execa('pnpm', ['--version']);
+ return exitCode === 0;
+ } catch {
+ return false;
+ }
+}
+
async function findPackageManagerFromLockFile(
rootDir: string,
): Promise<PackageManager | undefined> {
@@ -54,8 +72,8 @@ function findPackageManagerFromUserAgent(): PackageManager | undefined {
}
async function askForPackageManagerChoice(): Promise<PackageManager> {
- const hasYarn = shell.exec('yarn --version', {silent: true}).code === 0;
- const hasPnpm = shell.exec('pnpm --version', {silent: true}).code === 0;
+ const hasYarn = await hasYarnInPath();
+ const hasPnpm = await hasPnpmInPath();
if (!hasYarn && !hasPnpm) {
return 'npm';
@@ -464,7 +482,9 @@ export default async function init(
if (source.type === 'git') {
logger.info`Cloning Git template url=${source.url}...`;
const command = await getGitCommand(source.strategy);
- if (shell.exec(`${command} ${source.url} ${dest}`).code !== 0) {
+ try {
+ await execa(command, [source.url, dest]);
+ } catch {
logger.error`Cloning Git template failed!`;
process.exit(1);
}
@@ -514,26 +534,24 @@ export default async function init(
const cdpath = path.relative('.', dest);
const pkgManager = await getPackageManager(dest, cliOptions);
if (!cliOptions.skipInstall) {
- shell.cd(dest);
logger.info`Installing dependencies with name=${pkgManager}...`;
- if (
- shell.exec(
- pkgManager === 'yarn' ? 'yarn' : `${pkgManager} install --color always`,
- {
- env: {
- ...process.env,
- // Force coloring the output, since the command is invoked by
- // shelljs, which is not an interactive shell
- ...(supportsColor.stdout ? {FORCE_COLOR: '1'} : {}),
- },
+ try {
+ const args =
+ pkgManager === 'yarn' ? [] : ['install', '--color', 'always'];
+ await execa(pkgManager, args, {
+ cwd: dest,
+ env: {
+ // Force coloring the output, since the command is invoked by
+ // execa, which is not an interactive shell
+ ...(supportsColor.stdout ? {FORCE_COLOR: '1'} : {}),
},
- ).code !== 0
- ) {
+ }).stdout?.pipe(process.stdout);
+ } catch {
logger.error('Dependency installation failed.');
logger.info`The site directory has already been created, and you can retry by typing:
- code=${`cd ${cdpath}`}
- code=${`${pkgManager} install`}`;
+ code=${`cd ${cdpath}`}
+ code=${`${pkgManager} install`}`;
process.exit(0);
}
}
diff --git a/packages/docusaurus-utils/package.json b/packages/docusaurus-utils/package.json
index cdddacda8d..d76472ea9d 100644
--- a/packages/docusaurus-utils/package.json
+++ b/packages/docusaurus-utils/package.json
@@ -20,6 +20,7 @@
"dependencies": {
"@docusaurus/logger": "2.0.0-beta.20",
"@svgr/webpack": "^6.2.1",
+ "execa": "^6.1.0",
"file-loader": "^6.2.0",
"fs-extra": "^10.1.0",
"github-slugger": "^1.4.0",
@@ -29,10 +30,10 @@
"lodash": "^4.17.21",
"micromatch": "^4.0.5",
"resolve-pathname": "^3.0.0",
- "shelljs": "^0.8.5",
"tslib": "^2.4.0",
"url-loader": "^4.1.1",
- "webpack": "^5.72.1"
+ "webpack": "^5.72.1",
+ "which": "^2.0.2"
},
"engines": {
"node": ">=14"
@@ -43,6 +44,7 @@
"@types/github-slugger": "^1.3.0",
"@types/micromatch": "^4.0.2",
"@types/react-dom": "^18.0.3",
+ "@types/which": "^2.0.1",
"dedent": "^0.7.0"
}
}
diff --git a/packages/docusaurus-utils/src/gitUtils.ts b/packages/docusaurus-utils/src/gitUtils.ts
index 4d7ffde633..20a6b2a866 100644
--- a/packages/docusaurus-utils/src/gitUtils.ts
+++ b/packages/docusaurus-utils/src/gitUtils.ts
@@ -6,7 +6,11 @@
*/
import path from 'path';
-import shell from 'shelljs';
+import fs from 'fs-extra';
+import {execaSync} from 'execa';
+import which from 'which';
+
+import type {ExecaSyncError} from 'execa';
/** Custom error thrown when git is not found in `PATH`. */
export class GitNotFoundError extends Error {}
@@ -85,49 +89,52 @@ export function getFileCommitDate(
timestamp: number;
author?: string;
} {
- if (!shell.which('git')) {
+ if (!which.sync('git', {nothrow: true})) {
throw new GitNotFoundError(
`Failed to retrieve git history for "${file}" because git is not installed.`,
);
}
- if (!shell.test('-f', file)) {
+ // eslint-disable-next-line no-restricted-properties
+ if (!fs.existsSync(file)) {
throw new Error(
`Failed to retrieve git history for "${file}" because the file does not exist.`,
);
}
+ const args = ['log', '--max-count=1'];
+ if (age === 'oldest') {
+ // --follow is necessary to follow file renames
+ // --diff-filter=A ensures we only get the commit which (A)dded the file
+ args.push(' --follow --diff-filter=A');
+ }
+
let formatArg = '--format=%ct';
if (includeAuthor) {
formatArg += ',%an';
}
+ args.push(formatArg);
- let extraArgs = '--max-count=1';
- if (age === 'oldest') {
- // --follow is necessary to follow file renames
- // --diff-filter=A ensures we only get the commit which (A)dded the file
- extraArgs += ' --follow --diff-filter=A';
- }
+ args.push('--', path.basename(file));
- const result = shell.exec(
- `git log ${extraArgs} ${formatArg} -- "${path.basename(file)}"`,
- {
- // Setting cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
+ let result;
+ try {
+ result = execaSync('git', args, {
cwd: path.dirname(file),
- silent: true,
- },
- );
- if (result.code !== 0) {
+ });
+ } catch (_e) {
+ const error = _e as ExecaSyncError;
throw new Error(
- `Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`,
+ `Failed to retrieve the git history for file "${file}" with exit code ${error.exitCode}: ${error.stderr}`,
);
}
+
let regex = /^(?<timestamp>\d+)$/;
if (includeAuthor) {
regex = /^(?<timestamp>\d+),(?<author>.+)$/;
}
- const output = result.stdout.trim();
+ const output = result.stdout;
if (!output) {
throw new FileNotTrackedError(
diff --git a/yarn.lock b/yarn.lock
index 5374e67cd0..1ccc21ec01 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3817,6 +3817,11 @@
anymatch "^3.0.0"
source-map "^0.6.0"
+"@types/which@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@types/which/-/which-2.0.1.tgz#27ecd67f915b7c3d6ba552135bb1eecd66e63501"
+ integrity sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==
+
"@types/ws@^8.5.1":
version "8.5.3"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
@@ -7139,6 +7144,21 @@ execa@^5.0.0, execa@^5.1.1:
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
+execa@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20"
+ integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.1"
+ human-signals "^3.0.1"
+ is-stream "^3.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^5.1.0"
+ onetime "^6.0.0"
+ signal-exit "^3.0.7"
+ strip-final-newline "^3.0.0"
+
execall@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45"
@@ -7787,7 +7807,7 @@ get-stream@^5.1.0:
dependencies:
pump "^3.0.0"
-get-stream@^6.0.0:
+get-stream@^6.0.0, get-stream@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
@@ -8525,6 +8545,11 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+human-signals@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5"
+ integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==
+
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
@@ -9093,6 +9118,11 @@ is-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+is-stream@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
+ integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
+
is-string@^1.0.5, is-string@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
@@ -10599,6 +10629,11 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+mimic-fn@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
+ integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
+
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
@@ -11153,6 +11188,13 @@ npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
+npm-run-path@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00"
+ integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==
+ dependencies:
+ path-key "^4.0.0"
+
npm-to-yarn@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/npm-to-yarn/-/npm-to-yarn-1.0.1.tgz#6cdb95114c4ff0be50a7a2381d4d16131a5f52df"
@@ -11325,6 +11367,13 @@ onetime@^5.1.0, onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"
+onetime@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
+ integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
+ dependencies:
+ mimic-fn "^4.0.0"
+
open@^8.0.9, open@^8.4.0:
version "8.4.0"
resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
@@ -11681,6 +11730,11 @@ path-key@^3.0.0, path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+path-key@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
+ integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
+
path-parse@^1.0.6, path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
@@ -14297,6 +14351,11 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+strip-final-newline@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
+ integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
+
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment