This is a React page
26 |
56 |
57 |
58 |
59 | 🎉 Great! Your package.json will always be clean and tidy.
60 |
--------------------------------------------------------------------------------
/lib/commands/defaultCommand.js:
--------------------------------------------------------------------------------
1 | import prompts from 'prompts';
2 | import {getUserConfig} from '../getUserConfig.js';
3 | import {parseUserConfig} from '../parseUserConfig.js';
4 | import {runScript} from '../runScript.js';
5 |
6 | /**
7 | * @param {import('../types').ParsedUserConfig} parsedUserConfig
8 | */
9 | async function promptsAndRun(parsedUserConfig) {
10 | const choices = Object.values(parsedUserConfig).map(script => {
11 | if (!script) throw new Error('Failed parsing config script');
12 | return {
13 | title: script.alias ?? script.name,
14 | value: script,
15 | description: script.desc ?? script.command
16 | };
17 | });
18 |
19 | /** @type {{script: import('../types.js').Script}} */
20 | const {script} = await prompts({
21 | type: 'autocomplete',
22 | name: 'script',
23 | message: 'Select a script to run',
24 | suggest: (input, choices) =>
25 | Promise.resolve(
26 | choices.filter(choice =>
27 | choice.title.toLowerCase().includes(input.toLowerCase())
28 | )
29 | ),
30 | clearFirst: true,
31 | choices
32 | });
33 |
34 | // Exit if no script value
35 | if (!script) process.exit(0);
36 |
37 | // Run script recursively
38 | await runScript(script, nextScripts => {
39 | return promptsAndRun(nextScripts);
40 | });
41 | }
42 |
43 | export async function defaultCommand(argv) {
44 | const {config: configPath} = argv;
45 |
46 | const userConfig = await getUserConfig(configPath);
47 | const parsedUserConfig = parseUserConfig(userConfig);
48 |
49 | await promptsAndRun(parsedUserConfig);
50 | }
51 |
--------------------------------------------------------------------------------
/site/docs/getting-started/installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Installation
6 |
7 | :::tip
8 | The following instruction will use `yarn` as default pacakge manager, you can use `npm` or `pnpm` instead.
9 | :::
10 |
11 | ## Install locally
12 |
13 | `better-scripts` is recommend to be installed as a **dev** dependency.
14 |
15 | import Tabs from '@theme/Tabs';
16 | import TabItem from '@theme/TabItem';
17 |
18 |
4 |
5 | ## Previous
6 |
7 | Beforehand, we have npm scripts like this that need to add emoji and desc.
8 |
9 | ```json title="package.json"
10 | {
11 | "scripts": {
12 | "scripts": "better-scripts",
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "test": "react-scripts test"
16 | }
17 | }
18 | ```
19 |
20 | :::tip
21 | By default, better-scripts reads the `scripts` field in `package.json` if config is not found, but now we need to add some config for emoji and description.
22 | :::
23 |
24 | ## Add description
25 |
26 | Add a `better-scripts` field in `pacakge.json` and write command and description
27 |
28 | ```json title="package.json"
29 | {
30 | "scripts": {...},
31 | "better-scripts": {
32 | "start": ["react-scripts start", "Start a development server"],
33 | "build": ["react-scripts build", "Create a production build"],
34 | "test": ["react-scripts test", "Run tests"]
35 | }
36 | }
37 | ```
38 |
39 | > `scriptName`: [`command`, `desc`]
40 |
41 | ## Add emoji
42 |
43 | An array formed script value can only place command and desc, we need change it to an object in order to add emoji.
44 |
45 | ```json title="package.json"
46 | {
47 | "scripts": {...},
48 | "better-scripts": {
49 | "dev": {
50 | "alias": "🌟 Dev",
51 | "command": "yarn start",
52 | "desc": "Start a development server"
53 | },
54 | "build": {
55 | "alias": "📦 Build",
56 | "command": "yarn build",
57 | "desc": "Create a production build"
58 | },
59 | "test": {
60 | "alias": "🧪 Test",
61 | "command": "yarn test",
62 | "desc": "Run tests"
63 | }
64 | }
65 | }
66 | ```
67 |
--------------------------------------------------------------------------------
/site/src/components/components.js:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 | import {rgba} from 'polished';
3 |
4 | export const Header = styled.header`
5 | display: flex;
6 | align-items: center;
7 | justify-content: center;
8 | gap: 60px;
9 | margin-bottom: 60px;
10 | margin-top: 40px;
11 | text-align: center;
12 |
13 | @media screen and (max-width: 996px) {
14 | margin-top: 0;
15 | margin-bottom: 20px;
16 | }
17 | `;
18 |
19 | export const Title = styled.h1`
20 | font-size: 60px;
21 | font-weight: 800;
22 | color: transparent;
23 | background: linear-gradient(to right bottom, #5d3fd1, #d98df7);
24 | -webkit-background-clip: text;
25 | background-clip: text;
26 | letter-spacing: -0.03em;
27 |
28 | @media screen and (max-width: 996px) {
29 | font-size: 40px;
30 | }
31 | `;
32 |
33 | export const Caption = styled.p`
34 | /* font-weight: bold; */
35 | font-size: 16px;
36 | opacity: 0.5;
37 | margin-bottom: 10px;
38 |
39 | @media screen and (max-width: 996px) {
40 | font-size: 12px;
41 | }
42 | `;
43 |
44 | export const Tagline = styled.p`
45 | font-weight: bold;
46 | font-size: 18px;
47 | white-space: pre-wrap;
48 | opacity: 0.8;
49 |
50 | @media screen and (max-width: 996px) {
51 | font-size: 16px;
52 | }
53 | `;
54 |
55 | export const Main = styled.main`
56 | display: flex;
57 | flex-direction: column;
58 | width: 100%;
59 | max-width: 1280px;
60 | padding: 20px 80px;
61 | margin: 0 auto;
62 | margin-top: 40px;
63 |
64 | @media screen and (max-width: 996px) {
65 | padding: 10px 20px;
66 | }
67 | `;
68 |
69 | export const Image = styled.img`
70 | width: 100%;
71 | height: 100%;
72 | max-height: 400px;
73 | object-fit: contain;
74 | border-radius: 20px;
75 | box-shadow: 0 20px 20px ${rgba('#5d3fd1', 0.2)};
76 |
77 | @media screen and (max-width: 996px) {
78 | max-height: 200px;
79 | }
80 | `;
81 |
--------------------------------------------------------------------------------
/scripts/generate-md-contributors.js:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk';
2 | import m from 'mdast-builder';
3 | import {headingRange} from 'mdast-util-heading-range';
4 | import {readFileSync, writeFileSync} from 'node:fs';
5 | import {createRequire} from 'node:module';
6 | import remarkGfm from 'remark-gfm';
7 | import remarkParse from 'remark-parse';
8 | import remarkStringify from 'remark-stringify';
9 | import {unified} from 'unified';
10 | import {getContributors} from './utils/getContributors.js';
11 | import {writeContributorsAvatar} from './utils/writeContributorsAvatar.js';
12 |
13 | const require = createRequire(import.meta.url);
14 |
15 | const md = readFileSync(require.resolve('../README.md'), 'utf8');
16 |
17 | (async () => {
18 | await writeContributorsAvatar();
19 | const file = await unified()
20 | .use(remarkParse)
21 | .use(remarkGfm)
22 | .use(() => async (tree, file, next) => {
23 | const contributors = await getContributors();
24 | headingRange(tree, 'Contributors', (start, nodes, end) => [
25 | start,
26 | m.table('center', [
27 | m.tableRow(contributors.map(c => m.tableCell(m.text(c.login)))),
28 | m.tableRow(
29 | contributors.map(c =>
30 | m.tableCell(
31 | m.link(
32 | c.html_url,
33 | c.login,
34 | m.image(
35 | `./static/contributors/${c.login}.svg`,
36 | c.login,
37 | c.login
38 | )
39 | )
40 | )
41 | )
42 | )
43 | ]),
44 | end
45 | ]);
46 | next();
47 | })
48 | .use(remarkStringify)
49 | .process(md);
50 |
51 | const result = String(file);
52 |
53 | writeFileSync(require.resolve('../README.md'), result, 'utf8');
54 |
55 | console.log(
56 | chalk.green('📄 Contributors added:'),
57 | chalk.bgGreen(`${require.resolve('../README.md')}`)
58 | );
59 | })();
60 |
--------------------------------------------------------------------------------
/site/.example-docs/tutorial-extras/translate-your-site.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Translate your site
6 |
7 | Let's translate `docs/intro.md` to French.
8 |
9 | ## Configure i18n
10 |
11 | Modify `docusaurus.config.js` to add support for the `fr` locale:
12 |
13 | ```js title="docusaurus.config.js"
14 | module.exports = {
15 | i18n: {
16 | defaultLocale: 'en',
17 | locales: ['en', 'fr'],
18 | },
19 | };
20 | ```
21 |
22 | ## Translate a doc
23 |
24 | Copy the `docs/intro.md` file to the `i18n/fr` folder:
25 |
26 | ```bash
27 | mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
28 |
29 | cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
30 | ```
31 |
32 | Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
33 |
34 | ## Start your localized site
35 |
36 | Start your site on the French locale:
37 |
38 | ```bash
39 | npm run start -- --locale fr
40 | ```
41 |
42 | Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
43 |
44 | :::caution
45 |
46 | In development, you can only use one locale at a same time.
47 |
48 | :::
49 |
50 | ## Add a Locale Dropdown
51 |
52 | To navigate seamlessly across languages, add a locale dropdown.
53 |
54 | Modify the `docusaurus.config.js` file:
55 |
56 | ```js title="docusaurus.config.js"
57 | module.exports = {
58 | themeConfig: {
59 | navbar: {
60 | items: [
61 | // highlight-start
62 | {
63 | type: 'localeDropdown',
64 | },
65 | // highlight-end
66 | ],
67 | },
68 | },
69 | };
70 | ```
71 |
72 | The locale dropdown now appears in your navbar:
73 |
74 | 
75 |
76 | ## Build your localized site
77 |
78 | Build your site for a specific locale:
79 |
80 | ```bash
81 | npm run build -- --locale fr
82 | ```
83 |
84 | Or build your site to include all the locales at once:
85 |
86 | ```bash
87 | npm run build
88 | ```
89 |
--------------------------------------------------------------------------------
/bin/better-scripts.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import chalk from 'chalk';
4 | import yargs from 'yargs';
5 | import {hideBin} from 'yargs/helpers';
6 | import {defaultCommand} from '../lib/commands/defaultCommand.js';
7 | import {listCommand} from '../lib/commands/listCommand.js';
8 | import {runCommand} from '../lib/commands/runCommand.js';
9 | import {errorHandler} from '../lib/errorHandler.js';
10 | import {getPkg} from '../lib/getPkg.js';
11 |
12 | const pkg = getPkg();
13 |
14 | process.on('uncaughtException', errorHandler);
15 | process.on('unhandledRejection', errorHandler);
16 |
17 | if (hideBin(process.argv).includes('--track')) {
18 | process.env.TRACK_ERROR = 'true';
19 | }
20 |
21 | const argv = yargs(hideBin(process.argv))
22 | .usage(chalk.magenta('\n' + pkg.description))
23 | .option('config', {
24 | alias: 'c',
25 | desc: 'Specified config filepath',
26 | type: 'string'
27 | })
28 | .option('track', {
29 | desc: 'Show tracked error stack message'
30 | })
31 | .help('help')
32 | .alias('help', 'h')
33 | .alias('version', 'v')
34 | .command('$0', 'Run script interactively', {}, defaultCommand)
35 | .command(
36 | 'run
34 |
35 |
63 |
64 |
69 |
70 |
75 |
--------------------------------------------------------------------------------
/scripts/utils/writeContributorsAvatar.js:
--------------------------------------------------------------------------------
1 | import {existsSync, mkdirSync, writeFileSync} from 'node:fs';
2 | import path, {dirname} from 'node:path';
3 | import {fileURLToPath} from 'node:url';
4 | import {getContributors} from './getContributors.js';
5 |
6 | const __filename = fileURLToPath(import.meta.url);
7 | const __dirname = dirname(__filename);
8 |
9 | const dirpath = path.resolve(__dirname, '../../static/contributors');
10 |
11 | if (!existsSync(dirpath)) {
12 | mkdirSync(dirpath);
13 | }
14 |
15 | export function writeContributorsAvatar() {
16 | return getContributors().then(contributors => {
17 | contributors.map(c => {
18 | const {base64, login} = c;
19 | writeFileSync(
20 | path.resolve(dirpath, `${login}.svg`),
21 | `
62 | `
63 | );
64 | });
65 | });
66 | }
67 |
--------------------------------------------------------------------------------
/site/src/components/HomepageFeatures/index.js:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 | import clsx from 'clsx';
3 | import {size} from 'polished';
4 | import React from 'react';
5 | import styles from './styles.module.css';
6 |
7 | const FeatureList = [
8 | {
9 | title: 'Easy to Use',
10 | img: require('@site/static/img/Saly-38.png').default,
11 | description: (
12 | <>
13 | Better scripts can be used with zero-config. Though
14 | your can progressively add description, env, child scripts... for your
15 | scripts.
16 | >
17 | )
18 | },
19 | {
20 | title: 'Better DX',
21 | img: require('@site/static/img/Saly-39.png').default,
22 | description: (
23 | <>
24 | It provides you a better development experience with a simple command to
25 | start with.
26 | >
27 | )
28 | },
29 | {
30 | title: 'Consistent',
31 | img: require('@site/static/img/Saly-40.png').default,
32 | description: (
33 | <>
34 | Once you use better-scripts for all your projects, you will get a
35 | consistent scriping experience.
36 | >
37 | )
38 | }
39 | ];
40 |
41 | const ImageWrapper = styled.div`
42 | padding: 20px;
43 | background-color: rgb(114 109 197 / 8%);
44 | display: flex;
45 | justify-content: center;
46 | align-items: center;
47 | border-radius: 20px;
48 | margin-bottom: 30px;
49 | `;
50 |
51 | const Image = styled.img`
52 | ${size(240)}
53 | object-fit: contain;
54 |
55 | @media screen and (max-width: 996px) {
56 | ${size(180)}
57 | }
58 | `;
59 |
60 | function Feature({img, title, description}) {
61 | return (
62 | {description}
69 |17 | 18 | - `better-scripts.json` 19 | - `.better-scriptsrc` 20 | - `.better-scriptsrc.json` 21 | - `.better-scriptsrc.yaml` 22 | - `.better-scriptsrc.yml` 23 | - `.better-scriptsrc.js` 24 | - `.better-scriptsrc.cjs` 25 | - `better-scriptsrc.config.js` 26 | - `better-scriptsrc.config.cjs` 27 | 28 |
29 |A better way to organize your npm scripts
8 |See better-scripts.vercel.app →
9 |
12 |
13 |
67 |
68 |
104 |
105 |
124 |
125 |