diff --git a/.github/ISSUE_TEMPLATE/add-plugin.yml b/.github/ISSUE_TEMPLATE/add-plugin.yml new file mode 100644 index 0000000..62a2e03 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/add-plugin.yml @@ -0,0 +1,122 @@ +name: Add a Plugin +description: Submit a new plugin to the Docusaurus Community Plugin Directory. +title: "[Plugin] " +labels: + - plugin-submission +body: + - type: markdown + attributes: + value: | + Fill in the fields below. A bot will generate your YAML file and open a draft PR automatically. + Review the [contributing guide](https://docusaurus.community/contributing/plugins) before submitting. + + - type: input + id: name + attributes: + label: Plugin name + description: Display name shown in the directory. + placeholder: "My Awesome Plugin" + validations: + required: true + + - type: input + id: id + attributes: + label: Plugin ID + description: "`author.plugin-short-name` — must be unique. Lowercase letters, numbers, and hyphens only." + placeholder: "yourname.plugin-short-name" + validations: + required: true + + - type: textarea + id: description + attributes: + label: Description + description: One sentence describing what the plugin does. + placeholder: "A plugin that does X." + validations: + required: true + + - type: input + id: website + attributes: + label: Website / Repository URL + description: Public website or repository URL. + placeholder: "https://github.com/yourname/your-plugin" + validations: + required: true + + - type: input + id: source + attributes: + label: Source code URL + description: Source code URL. Required for open-source plugins — leave blank for closed-source. + placeholder: "https://github.com/yourname/your-plugin" + + - type: input + id: author + attributes: + label: Author + description: Your GitHub username or organisation name. + placeholder: "yourname" + + - type: input + id: preview + attributes: + label: Preview image URL + description: URL to a preview image. Leave blank to auto-generate. + placeholder: "https://..." + + - type: dropdown + id: status + attributes: + label: Status + description: Current maintenance status of the plugin. + options: + - maintained + - unmaintained + - unknown + validations: + required: true + + - type: input + id: minimumVersion + attributes: + label: Minimum Docusaurus version + description: e.g. `3.0.0`. Leave blank if not applicable. + placeholder: "3.0.0" + + - type: checkboxes + id: tags + attributes: + label: Tags + description: Select all that apply. + options: + - label: search + - label: api + - label: utility + - label: content + - label: theme + - label: markdown + - label: analytics + - label: integration + - label: seo + - label: editing + - label: docusaurus + + - type: textarea + id: npmPackages + attributes: + label: npm package name(s) + description: One package name per line. + placeholder: "your-npm-package-name" + + - type: checkboxes + id: checklist + attributes: + label: Checklist + options: + - label: The plugin works with a current or recent stable release of Docusaurus. + required: true + - label: I have read the [contributing guide](https://docusaurus.community/contributing/plugins). + required: true diff --git a/.github/workflows/create-plugin-pr.yml b/.github/workflows/create-plugin-pr.yml new file mode 100644 index 0000000..b333f6d --- /dev/null +++ b/.github/workflows/create-plugin-pr.yml @@ -0,0 +1,197 @@ +name: Create Plugin PR from Issue + +on: + issues: + types: [opened] + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + create-pr: + if: contains(github.event.issue.labels.*.name, 'plugin-submission') + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + const body = context.payload.issue.body ?? ''; + const issueNumber = context.payload.issue.number; + const issueUrl = context.payload.issue.html_url; + + function parseField(body, heading) { + const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const regex = new RegExp(`### ${escaped}\\r?\\n\\r?\\n([\\s\\S]*?)(?=\\r?\\n### |$)`); + const match = body.match(regex); + if (!match) return null; + const val = match[1].trim(); + return val === '_No response_' || val === '' ? null : val; + } + + function parseCheckboxes(body, heading) { + const section = parseField(body, heading); + if (!section) return []; + return section.split('\n') + .filter(line => /^- \[x\]/i.test(line.trim())) + .map(line => line.replace(/^- \[x\] /i, '').trim()); + } + + const name = parseField(body, 'Plugin name'); + const id = parseField(body, 'Plugin ID'); + const description = parseField(body, 'Description'); + const website = parseField(body, 'Website / Repository URL'); + const source = parseField(body, 'Source code URL'); + const author = parseField(body, 'Author'); + const preview = parseField(body, 'Preview image URL'); + const status = parseField(body, 'Status'); + const minimumVersion = parseField(body, 'Minimum Docusaurus version'); + const npmPackagesRaw = parseField(body, 'npm package name(s)'); + const tags = parseCheckboxes(body, 'Tags'); + + const errors = []; + if (!name) errors.push('Plugin name is required'); + if (!id) errors.push('Plugin ID is required'); + if (id && !/^[a-z0-9-]+\.[a-z0-9-]+$/.test(id)) errors.push('Plugin ID must be in `author.plugin-name` format using lowercase letters, numbers, and hyphens only'); + if (!description) errors.push('Description is required'); + if (!website) errors.push('Website / Repository URL is required'); + if (!status) errors.push('Status is required'); + + if (errors.length > 0) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: [ + 'Thank you for your plugin submission! Unfortunately some required fields are missing or invalid:', + '', + ...errors.map(e => `- ${e}`), + '', + 'Please close this issue and open a new one with all fields correctly filled in.', + ].join('\n'), + }); + return; + } + + const npmPackages = npmPackagesRaw + ? npmPackagesRaw.split('\n').map(l => l.trim()).filter(Boolean) + : []; + + const filename = `${id}.yaml`; + const branch = `plugin/${id}`; + + // Check if the file already exists + try { + await github.rest.repos.getContent({ + owner: context.repo.owner, + repo: context.repo.repo, + path: `data/plugins/${filename}`, + }); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: `A plugin with ID \`${id}\` already exists in the directory. Please choose a unique ID, then close this issue and open a new one.`, + }); + return; + } catch (e) { + // File doesn't exist — expected + } + + // Generate YAML + const yamlLines = [ + '# yaml-language-server: $schema=https://docusaurus.community/schema/plugin/1.0.0.json', + `id: ${id}`, + `name: "${name.replace(/"/g, '\\"')}"`, + `description: "${description.replace(/"/g, '\\"')}"`, + `preview: ${preview || 'null'}`, + `website: ${website}`, + `source: ${source || 'null'}`, + `author: ${author || 'null'}`, + ]; + + if (tags.length > 0) { + yamlLines.push('tags:'); + tags.forEach(t => yamlLines.push(` - ${t}`)); + } else { + yamlLines.push('tags: []'); + } + + yamlLines.push(`minimumVersion: ${minimumVersion || 'null'}`); + yamlLines.push(`status: ${status}`); + + if (npmPackages.length > 0) { + yamlLines.push('npmPackages:'); + npmPackages.forEach(p => yamlLines.push(` - ${p}`)); + } else { + yamlLines.push('npmPackages: []'); + } + + const yaml = yamlLines.join('\n') + '\n'; + + // Get main branch SHA + const mainRef = await github.rest.git.getRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'heads/main', + }); + const baseSha = mainRef.data.object.sha; + + // Create branch, falling back to a unique name if it already exists + let actualBranch = branch; + try { + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/heads/${branch}`, + sha: baseSha, + }); + } catch (e) { + actualBranch = `${branch}-${issueNumber}`; + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/heads/${actualBranch}`, + sha: baseSha, + }); + } + + // Commit the YAML file + await github.rest.repos.createOrUpdateFileContents({ + owner: context.repo.owner, + repo: context.repo.repo, + path: `data/plugins/${filename}`, + message: `chore: add plugin ${id} from issue #${issueNumber}`, + content: Buffer.from(yaml).toString('base64'), + branch: actualBranch, + }); + + // Open a draft PR + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `Add plugin: ${name}`, + head: actualBranch, + base: 'main', + body: [ + `Adds \`${id}\` to the plugin directory.`, + '', + `Generated automatically from issue ${issueUrl}.`, + '', + `Closes #${issueNumber}`, + ].join('\n'), + draft: true, + }); + + // Comment on the issue with the PR link + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: [ + `Thank you for your plugin submission! A draft PR has been created: ${pr.data.html_url}`, + '', + 'A maintainer will review it shortly. Please check the PR for any CI validation errors.', + ].join('\n'), + }); diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 07e77d1..faa48b6 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/contributing/plugins.mdx b/contributing/plugins.mdx index 34517de..a0f1400 100644 --- a/contributing/plugins.mdx +++ b/contributing/plugins.mdx @@ -8,8 +8,9 @@ The plugin directory is powered by [`@homotechsual/docusaurus-plugin-showcase`]( ## Before you submit -- The plugin must be **open source** with a publicly accessible source repository. -- The plugin must work with a current or recent stable release of Docusaurus. +- If the plugin is **open source** the source repository must be publicly accessible and present in the `source` field of your YAML file. +- If the plugin is **closed source** the source repository must not be present in the `source` field of your YAML file. +- The plugin must work with a current or recent stable release of Docusaurus to have `status` set to `maintained`. If the plugin is no longer maintained, set `status` to `unmaintained`. If you are unsure, set `status` to `unknown`. - One entry per npm package (or logical plugin unit). ## Create your YAML file @@ -55,6 +56,12 @@ npmPackages: ## Submit a pull request +### Option A — automated (recommended) + +Open a [plugin submission issue](https://github.com/DocusaurusCommunity/website/issues/new?template=add-plugin.yml) and fill in the form. A bot will generate your YAML file, open a draft PR, and comment on the issue with a link. You do not need to fork the repository. + +### Option B — manual + 1. Fork [`DocusaurusCommunity/website`](https://github.com/DocusaurusCommunity/website) on GitHub. 2. Add your YAML file to `data/plugins/`. 3. Open a pull request against the `main` branch. diff --git a/docs/component-library/new/Card/index.mdx b/docs/component-library/new/Card/index.mdx index 4025133..db13e04 100644 --- a/docs/component-library/new/Card/index.mdx +++ b/docs/component-library/new/Card/index.mdx @@ -635,8 +635,10 @@ For convenience these cards are displayed using some wrapper div elements like a
Docux (@Juniors017)
- humble contributor on: - docusaurus.community + {'humble contributor on: '} + + {'docusaurus.community'} +
@@ -721,8 +723,10 @@ For convenience these cards are displayed using some wrapper div elements like a
Docux (@Juniors017)
- humble contributor on: - docusaurus.community + {'humble contributor on: '} + + {'docusaurus.community'} +
@@ -914,9 +918,7 @@ For convenience these cards are displayed using some wrapper div elements like a />
Docux (@Juniors017)
- - humble contributor - + {'humble contributor'}
@@ -972,8 +974,10 @@ For convenience these cards are displayed using some wrapper div elements like a
Docux (@Juniors017)
- humble contributor on: - docusaurus.community + {'humble contributor on: '} + + {'docusaurus.community'} +
@@ -1006,8 +1010,10 @@ For convenience these cards are displayed using some wrapper div elements like a
Docux (@Juniors017)
- humble contributor on: - docusaurus.community + {'humble contributor on: '} + + {'docusaurus.community'} +
diff --git a/package.json b/package.json index d9a81f8..3b13b13 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@fortawesome/free-solid-svg-icons": "^7.3.0", "@fortawesome/react-fontawesome": "^3.3.1", "@homotechsual/docusaurus-plugin-plausible": "^1.1.1", - "@homotechsual/docusaurus-plugin-showcase": "1.1.1", + "@homotechsual/docusaurus-plugin-showcase": "1.2.0", "@iconify/react": "^6.0.2", "@mdx-js/react": "^3.1.1", "@popperjs/core": "^2.11.8", @@ -61,12 +61,12 @@ "@types/node": "^26.0.1", "@types/react": "^19.2.17", "jest": "^30.4.2", - "prettier": "3.8.4", + "prettier": "3.9.3", "prettier-plugin-css-order": "2.2.0", "prop-types": "^15.8.1", "react-waypoint": "^10.3.0", "typescript": "^6.0.3", - "webpack": "^5.108.0" + "webpack": "^5.108.2" }, "browserslist": { "production": [ diff --git a/yarn.lock b/yarn.lock index 704d7fd..df1ac33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4446,9 +4446,9 @@ __metadata: languageName: node linkType: hard -"@homotechsual/docusaurus-plugin-showcase@npm:1.1.1": - version: 1.1.1 - resolution: "@homotechsual/docusaurus-plugin-showcase@npm:1.1.1" +"@homotechsual/docusaurus-plugin-showcase@npm:1.2.0": + version: 1.2.0 + resolution: "@homotechsual/docusaurus-plugin-showcase@npm:1.2.0" dependencies: "@docusaurus/faster": "npm:^3.10.1" "@popperjs/core": "npm:^2.11.8" @@ -4462,7 +4462,7 @@ __metadata: "@docusaurus/core": ">=3.0.0 <5.0.0" react: ~19.2.7 react-dom: ~19.2.7 - checksum: 10c0/f68ffb99f5e296204533609986ba076eee903bf646b9eaff3230da1fe5306ed41717d9f0c291f9e8e6bdc04d24ab93835f6c77da8368576dc7286d77da8e0d35 + checksum: 10c0/9b3e306de08751df35d76d4cdd1c1eb02460b880e9139b029a039947393a2217e3b96e913f4c766ae10e660edae9475efd045c887e5c2ab1c6f03106a21096d4 languageName: node linkType: hard @@ -9836,7 +9836,7 @@ __metadata: "@fortawesome/free-solid-svg-icons": "npm:^7.3.0" "@fortawesome/react-fontawesome": "npm:^3.3.1" "@homotechsual/docusaurus-plugin-plausible": "npm:^1.1.1" - "@homotechsual/docusaurus-plugin-showcase": "npm:1.1.1" + "@homotechsual/docusaurus-plugin-showcase": "npm:1.2.0" "@iconify/react": "npm:^6.0.2" "@mdx-js/react": "npm:^3.1.1" "@popperjs/core": "npm:^2.11.8" @@ -9850,7 +9850,7 @@ __metadata: docusaurus-remark-plugin-tab-blocks: "npm:^4.0.0" git-url-parse: "npm:^16.1.0" jest: "npm:^30.4.2" - prettier: "npm:3.8.4" + prettier: "npm:3.9.3" prettier-plugin-css-order: "npm:2.2.0" prism-react-renderer: "npm:^2.4.1" prop-types: "npm:^15.8.1" @@ -9866,7 +9866,7 @@ __metadata: sass: "npm:^1.101.0" swc-loader: "npm:^0.2.7" typescript: "npm:^6.0.3" - webpack: "npm:^5.108.0" + webpack: "npm:^5.108.2" dependenciesMeta: "@docusaurus/tsconfig": unplugged: true @@ -16707,12 +16707,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:3.8.4": - version: 3.8.4 - resolution: "prettier@npm:3.8.4" +"prettier@npm:3.9.3": + version: 3.9.3 + resolution: "prettier@npm:3.9.3" bin: prettier: bin/prettier.cjs - checksum: 10c0/b90a0cbe75b88ac0af9c13fe0f359bd19926fabccd88483227b21f71f0c1cc42da056fc1ac3a361e665577c568371d5ccfb2c62c31c8a1186f8d1bd531a063e9 + checksum: 10c0/56623a03f0a1a791413b76458dbcab1dff8e5a7d02744be98ccc589a3a8cf8371dda8fba68f2d2336ded8475ed40f6eaf66f0f4d39df33235656018f64d2fdac languageName: node linkType: hard @@ -19745,9 +19745,9 @@ __metadata: languageName: node linkType: hard -"webpack@npm:^5.108.0": - version: 5.108.0 - resolution: "webpack@npm:5.108.0" +"webpack@npm:^5.108.2": + version: 5.108.2 + resolution: "webpack@npm:5.108.2" dependencies: "@types/estree": "npm:^1.0.8" "@types/json-schema": "npm:^7.0.15" @@ -19776,7 +19776,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 10c0/334e692b44575d19c3a0315cca78ab03314386a08f359fcb3f5ae18ea4d4c66bef673a1e7da411fa7de484bb51a0b154110f76bbda6a2638b3c193a781aee7ed + checksum: 10c0/0415339267d6759502022d0fe9a18f59d8c5d5bd08c42566f6591b86814073ee63078ad90344e45ed8bd6f2de13d58623a98b4bf47a620fca356b997da3d7f74 languageName: node linkType: hard