diff --git a/.editorconfig b/.editorconfig index b03beab..3fefca2 100755 --- a/.editorconfig +++ b/.editorconfig @@ -1,21 +1,19 @@ # http://editorconfig.org -root = true +root=true [*] -charset = utf-8 -end_of_line = lf -indent_style = space -indent_size = 4 -trim_trailing_whitespace = true -insert_final_newline = true +charset=utf-8 +end_of_line=lf +indent_style=space +indent_size=4 +trim_trailing_whitespace=true +insert_final_newline=true -# Use 2 spaces since npm does not respect custom indentation settings -[package.json] -indent_style = space -indent_size = 2 +[*.md] +trim_trailing_whitespace=false # Use 2 spaces since npm does not respect custom indentation settings -[**/package-definition.json] -indent_style = space -indent_size = 2 +[package.json] +indent_style=space +indent_size=2 diff --git a/.github/workflows/on-commit-build.yml b/.github/workflows/on-commit-build.yml index 640e2a4..108f04f 100644 --- a/.github/workflows/on-commit-build.yml +++ b/.github/workflows/on-commit-build.yml @@ -17,7 +17,9 @@ jobs: uses: actions/setup-node@v4 with: node-version: '20' - env: - HUSKY: 0 - name: Installing project dependencies run: npm install + - name: Build the code + run: npm start build + env: + CI: true diff --git a/.github/workflows/on-commit-test.yml b/.github/workflows/on-commit-test.yml index 450ba8e..684fcff 100644 --- a/.github/workflows/on-commit-test.yml +++ b/.github/workflows/on-commit-test.yml @@ -10,6 +10,8 @@ jobs: test: name: Run tests runs-on: ubuntu-latest + env: + NODE_OPTIONS: --experimental-modules steps: - name: Checkout code uses: actions/checkout@v4 @@ -17,9 +19,9 @@ jobs: uses: actions/setup-node@v4 with: node-version: '20' - env: - HUSKY: 0 - name: Installing project dependencies run: npm install - - name: Linting the code - run: npm start lint + - name: Test the code + run: npm start test + env: + CI: true diff --git a/.github/workflows/on-tag-publish-docs.yml b/.github/workflows/on-tag-publish-docs.yml index 1f00eef..ad643f9 100644 --- a/.github/workflows/on-tag-publish-docs.yml +++ b/.github/workflows/on-tag-publish-docs.yml @@ -28,8 +28,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: '20' - env: - HUSKY: 0 - name: Installing project dependencies run: npm install - name: Building the docs diff --git a/.github/workflows/on-tag-release-github.yml b/.github/workflows/on-tag-release-github.yml index 40a7ee1..54cd10e 100644 --- a/.github/workflows/on-tag-release-github.yml +++ b/.github/workflows/on-tag-release-github.yml @@ -16,7 +16,6 @@ jobs: with: node-version: '20' env: - HUSKY: 0 NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Installing project dependencies run: npm install diff --git a/.github/workflows/on-tag-release-npm.yml b/.github/workflows/on-tag-release-npm.yml index 7309b9d..9a20e00 100644 --- a/.github/workflows/on-tag-release-npm.yml +++ b/.github/workflows/on-tag-release-npm.yml @@ -16,7 +16,6 @@ jobs: with: node-version: '20' env: - HUSKY: 0 NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Installing project dependencies run: npm install diff --git a/.gitignore b/.gitignore index 884e907..114ef15 100755 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ -# OS generated files # +# OS generated files .DS_Store Thumbs.db .tmp/ -# IDE generated file # +# IDE generated file .idea/ # PackageManager specific lock files @@ -28,7 +28,3 @@ dist/ # Docs generated folder docs/ - -# Verdaccio storage -test/verdaccio/storage - diff --git a/.husky/commit-msg b/.husky/commit-msg index 43abf00..a7dcb45 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,8 +1,8 @@ # Read parameters -COMMIT_MSG_FILE=$1; +COMMIT_MSG_FILE=$1 # Set failing on command fail, and undefined variable use -set -eu; +set -eu # This hook is invoked by git-commit and git-merge, and can be # bypassed with the --no-verify option. It takes a single @@ -23,10 +23,10 @@ set -eu; # message sent has the correct format for the project, aborting otherwise. # Show welcome message -echo "**************************"; -echo "Linting the commit message"; -echo "**************************"; -echo ""; +echo "**************************" +echo "Linting the commit message" +echo "**************************" +echo "" # Run commitlint -npx --no -- commitlint --edit ${1}; +npx --no -- commitlint --edit ${1} diff --git a/.husky/pre-commit b/.husky/pre-commit index 8b7906f..70e0926 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,7 +2,7 @@ # NOTHING TO READ HERE # Set failing on command fail, and undefined variable use -set -eu; +set -eu # This hook is invoked by git-commit, and can be bypassed with the # --no-verify option. It takes no parameters, and is invoked before @@ -26,26 +26,29 @@ set -eu; # Generated files are added to the commit. # Show welcome message -echo "**************************"; -echo "Running pre commit hooks"; -echo "**************************"; -echo ""; +echo "**************************" +echo "Running pre commit hooks" +echo "**************************" +echo "" # Run license -echo "\nAdd license text to code files\n"; -npx --no -- nps license; - -# Run prettify -echo "Run prettify\n"; -npx --no -- nps prettify; +echo "\nAdd license text to code files\n" +npx --no -- gobstones-scripts run license --silent # Run changelog -echo "\nRun changelog\n"; -npx --no -- nps changelog; +echo "\nRun changelog\n" +# Only run if not first commit +if git-log &> /dev/null; then + npx --no -- gobstones-scripts run changelog --silent +fi + +# Run prettify +echo "Run prettify\n" +npx --no -- gobstones-scripts run prettify --silent # Add all generated files -echo "\nAdd generated files to commit\n"; -git add --all; +echo "\nAdd generated files to commit\n" +git add --all # Exit -exit 0; +exit 0 diff --git a/.husky/pre-push b/.husky/pre-push index 0e7fe23..305a10c 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,19 +1,23 @@ # Read parameters -REMOTE_NAME=$1; -REMOTE_URL=$2; +REMOTE_NAME=$1 +REMOTE_URL=$2 # Read the input and split it approprietly -INPUT=$(cat); -COUNTER=0; +INPUT=$(cat) +COUNTER=0 for WORD in $INPUT; do case $COUNTER in 0) - LOCAL_REF=$WORD;; + LOCAL_REF=$WORD + ;; 1) - LOCAL_OBJ_NAME=$WORD;; + LOCAL_OBJ_NAME=$WORD + ;; 2) - REMOTE_REF=$WORD;; + REMOTE_REF=$WORD + ;; 3) - REMOTE_OBJ_NAME=$WORD;; + REMOTE_OBJ_NAME=$WORD + ;; esac COUNTER=$((COUNTER + 1)) done @@ -21,14 +25,16 @@ done case "$LOCAL_REF" in refs\/tags*) # Apply specific code when publishing tags - IS_TAG=1;; + IS_TAG=1 + ;; *) # Apply specific code when publishing any branch - IS_TAG=0;; + IS_TAG=0 + ;; esac # Set failing on command fail, and undefined variable use -set -eu; +set -eu # This hook is called by git-push and can be used to prevent a # push from taking place. The hook is called with two parameters @@ -65,13 +71,13 @@ set -eu; # to match that of the tag. # Show welcome message -echo "**************************"; -echo "Running pre push hooks"; -echo "**************************"; -echo ""; +echo "**************************" +echo "Running pre push hooks" +echo "**************************" +echo "" # Run all the tests -echo "Running tests"; -npx --no -- nps test; +echo "Running tests" +npx --no -- gobstones-scripts run test --silent -exit 0; +exit 0 diff --git a/.husky/prepare-commit-msg b/.husky/prepare-commit-msg index 447317e..f68c5d7 100755 --- a/.husky/prepare-commit-msg +++ b/.husky/prepare-commit-msg @@ -1,10 +1,10 @@ # Read parameters -COMMIT_MSG_FILE=$1; -COMMIT_SOURCE=$2; -SHA1=$3; +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 # Set failing on command fail, and undefined variable use -set -eu; +set -eu # This hook is invoked by git-commit right after preparing # the default log message, and before the editor is started. @@ -34,44 +34,44 @@ set -eu; # semantic commit message through a prompt. # Show welcome message -echo "**************************"; -echo "Generate commit message"; -echo "**************************"; -echo ""; +echo "**************************" +echo "Generate commit message" +echo "**************************" +echo "" # Check if we are doing an amend -if [ "$COMMIT_SOURCE" = 'commit' ] && [ -n "$SHA1" ]; then +if [ "$COMMIT_SOURCE" = 'commit' ] && [ -n $SHA1 ]; then # Amends do not call the "cz" command. - exit 0; + exit 0 fi # Check if we are doing an squash if [ "$COMMIT_SOURCE" = 'squash' ]; then # Squashes should not trigger the cz command. - exit 0; + exit 0 fi # Check if we are doing an merge if [ "$COMMIT_SOURCE" = 'merge' ]; then # Merging should not trigger the cz command. - exit 0; + exit 0 fi # Check if we are using a template if [ "$COMMIT_SOURCE" = 'template' ]; then # Cannot use templates, warn the user and fail. - echo "Setting a template through -t or commit.template in this project "; - echo "has been disabled. Please run a simple commit.\n"; - exit 1; + echo "Setting a template through -t or commit.template in this project " + echo "has been disabled. Please run a simple commit.\n" + exit 1 fi # Check if a message was given if [ "$COMMIT_SOURCE" = 'message' ]; then # Cannot use message, warn the user and fail. - echo "Setting a message through -m or -F option in this project "; - echo "has been disabled. Please run a simple commit.\n"; - exit 1; + echo "Setting a message through -m or -F option in this project " + echo "has been disabled. Please run a simple commit.\n" + exit 1 fi # Regular commit has been performed, run the command. -exec < /dev/tty && npx cz --hook || true; +exec < /dev/tty && npx cz --hook || true diff --git a/.npmignore b/.npmignore index 9fe2874..5ef1a1c 100644 --- a/.npmignore +++ b/.npmignore @@ -1,18 +1,25 @@ # Editor and style files +.editorconfig +.prettierignore +.prettierrc +.eslintrc.js .vscode/* .github/* .husky/* # tests test/* +jest.config.js coverage # docs docs +# Building Configuration Files +webpack.config.js +rollup.config.js +tsconfig.json +package-scripts.js + # Additional documentation TODO.md - -# Add files in init-files -!init-files/* -!config/* diff --git a/.prettierignore b/.prettierignore deleted file mode 100755 index 3c8ac75..0000000 --- a/.prettierignore +++ /dev/null @@ -1,23 +0,0 @@ -# Node files # -node_modules/ -npm-debug.log -yarn-error.log -.rollup.cache - -# OS generated files # -.DS_Store -Thumbs.db -.tmp/ - -# Dist # -dist/ -coverage/ -tsconfig.build.json - -# IDE # -.idea/ - -# PackageManager specific # -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/.prettierrc b/.prettierrc new file mode 100755 index 0000000..6e9466b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,73 @@ +{ + "plugins": ["prettier-plugin-ini", "prettier-plugin-sh"], + "printWidth": 120, + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true, + "endOfLine": "lf", + "trailingComma": "none", + "overrides": [ + { + "files": ["*.js", "**/*.js", "*.jsx", "**/*.jsx"], + "options": { + "parser": "babel" + } + }, + { + "files": ["*.ts", "**/*.ts", "*.tsx", "**/*.tsx"], + "options": { + "parser": "typescript" + } + }, + { + "files": ["*.yml", "**/*.yml", "*.yaml", "**/*.yaml"], + "options": { + "parser": "yaml" + } + }, + { + "files": ["*.json", "**/*.json", ".czrc", "**/.czrc"], + "options": { + "parser": "json" + } + }, + { + "files": ["package.json"], + "options": { + "tabWidth": 2, + "parser": "json" + } + }, + { + "files": ["*.md", "**/*.md"], + "options": { + "parser": "markdown" + } + }, + { + "files": ["*.css", "**/*.css", "*.scss", "**/*.scss", "*.less", "**/*.less"], + "options": { + "parser": "css" + } + }, + { + "files": ["*.html", "**/*.html"], + "options": { + "parser": "html" + } + }, + { + "files": ["**/*.ini", ".editorconfig", ".npmrc"], + "options": { + "parser": "ini" + } + }, + { + "files": [".gitignore", ".npmignore", ".husky/*"], + "options": { + "parser": "sh" + } + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 02a9462..2ed3da7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,7 @@ -## 0.2.1 (2024-08-09) - -* feat: make theme work with default plugins and configuration ([87d03be](https://github.com/gobstones/typedoc-theme-gobstones/commit/87d03be)) +## 0.3.0 (2024-09-04) +## 0.2.1 (2024-08-09) +- feat: make theme work with default plugins and configuration ([87d03be](https://github.com/gobstones/typedoc-theme-gobstones/commit/87d03be)) ## 0.2.0 (2024-08-08) - - - - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce20137..d0d19d4 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,40 +1,7 @@ -# Contributing +# Contibution Guidelines -**I would be happy if you can contribute to this project!** If you feel you are able to provide improvements or bug fixes by yourself, please do so by forking the repository and creating a pull request. Otherwise feel free to create an issue. +Please, before contributing to this project, take a moment to read our [Contributions Guidelines](https://gobstones.github.io/gobstones-guidelines/). -## Local development - -Please use the following tools for development: - -- [NodeJS v20](https://nodejs.org/) -- [pnpm](https://pnpm.io/) -- [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) -- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) -- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - -## Commit messages - -For a more understandable collaboration, please use [conventional commit messages.](https://www.conventionalcommits.org/en/v1.0.0/) - -## Pull request - -Note the following to create a pull request. - -Run the linter and fix any linting problems with your code: - -```sh -pnpm lint -``` - -Document your changes in the `CHANGELOG.md`. - -Create a pull request. Ideally your pull request should include these things: - -- Tests for the things you've changed/added -- Good documentation / Comments in the code - -Now submit your pull request to merge from your fork to master. Then I will look at your request and if necessary suggest changes, improvements or alternatives. - ---- - -## :heart: I thank you for wanting to contribute to this project in order to create an improvement for all users. +Also, be sure to understand that by contributing you agree to allow +the project owners to license your work under the terms of our +[License](https://gobstones.github.io/gobstones-guidelines/LICENSE/). diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 index 4a66797..c248627 --- a/LICENSE +++ b/LICENSE @@ -4,7 +4,8 @@ Copyright (C) National University of Quilmes 2018-2024 Gobstones (TM) is a trademark of the National University of Quilmes. This program is free software distributed under the terms of the -GNU Affero General Public License version 3 solely. Additional terms added in compliance to section 7 of such license apply. +GNU Affero General Public License version 3 solely. +Additional terms added in compliance to section 7 of such license apply. Additional terms are listed next. Below them you may find a full copy of the GNU Affero General Public License version 3. diff --git a/README.md b/README.md index 5afae40..dcfe9dc 100755 --- a/README.md +++ b/README.md @@ -2,10 +2,11 @@ This a customized theme for [TypeDoc](https://typedoc.org/) used for all the generated documentation throughout the **GobstonesWeb2** project. It's based on the default theme used by TypeDoc, with mild improvements in readability and styling, and includes by default several plugins built-in so the user does not require to include them in their configuration. Included plugins are: - * [typedoc-plugin-mdn-links](https://github.com/Gerrit0/typedoc-plugin-mdn-links) - * [typedoc-plugin-merge-modules](https://github.com/krisztianb/typedoc-plugin-merge-modules) - * [typedoc-plugin-remove-references](https://github.com/eyworldwide/typedoc-plugin-remove-references) - * [typedoc-plugin-missing-exports](https://github.com/Gerrit0/typedoc-plugin-missing-exports) (With modifications) + +- [typedoc-plugin-mdn-links](https://github.com/Gerrit0/typedoc-plugin-mdn-links) +- [typedoc-plugin-merge-modules](https://github.com/krisztianb/typedoc-plugin-merge-modules) +- [typedoc-plugin-remove-references](https://github.com/eyworldwide/typedoc-plugin-remove-references) +- [typedoc-plugin-missing-exports](https://github.com/Gerrit0/typedoc-plugin-missing-exports) (With modifications) [![Licence](https://img.shields.io/badge/AGPL--3.0_with_additional_terms-olivegreen?style=plastic&label=License&logo=open-source-initiative&logoColor=white&color=olivegreen)](https://github.com/gobstones/typedoc-theme-gobstones/blob/main/LICENSE) [![Version](https://img.shields.io/github/package-json/v/gobstones/typedoc-theme-gobstones?style=plastic&label=Version&logo=git-lfs&logoColor=white&color=crimson)](https://www.npmjs.com/package/@gobstones/typedoc-theme-gobstones) @@ -40,10 +41,10 @@ module.exports = { ], excludeTags: [ // Remove the @internal from the excluded tags - "@override", - "@virtual", - "@satisfies", - "@overload", + '@override', + '@virtual', + '@satisfies', + '@overload' ], visibilityFilters: { // Add @internal as a visibility filter diff --git a/commitlint.config.js b/commitlint.config.mjs similarity index 73% rename from commitlint.config.js rename to commitlint.config.mjs index a989bfc..115abd6 100644 --- a/commitlint.config.js +++ b/commitlint.config.mjs @@ -1,3 +1,3 @@ -module.exports = { +export default { extends: ['@commitlint/config-conventional'] }; diff --git a/eslint.config.mjs b/eslint.config.mjs index cd45884..ace463d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,8 +1,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; -// eslint-disable-next-line import/namespace -import { fixupPluginRules, includeIgnoreFile } from '@eslint/compat'; +import { includeIgnoreFile } from '@eslint/compat'; import { FlatCompat } from '@eslint/eslintrc'; import eslintJs from '@eslint/js'; import eslintPluginNoNull from 'eslint-plugin-no-null'; @@ -11,51 +10,53 @@ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended' import globals from 'globals'; import eslintTs from 'typescript-eslint'; -// region Compatibility with old plugins section +const baseUrl = path.resolve(path.dirname(fileURLToPath(import.meta.url))); + +const tsConfigPath = path.join(baseUrl, 'tsconfig.json'); +const gitignorePath = path.join(baseUrl, '.gitignore'); + const compat = new FlatCompat({ - baseDirectory: path.dirname(fileURLToPath(import.meta.url)), + baseDirectory: baseUrl, recommendedConfig: eslintJs.configs.recommended }); -/** - * @param {string} name the pugin name - * @param {string} alias the plugin alias - * @returns {import("eslint").ESLint.Plugin} - */ -// eslint-disable-next-line no-unused-vars -function legacyPlugin(name, alias = name) { - const plugin = compat.plugins(name)[0]?.plugins?.[alias]; - - if (!plugin) { - throw new Error(`Unable to resolve plugin ${name} and/or alias ${alias}`); - } - - return fixupPluginRules(plugin); -} - -const gitignorePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '.gitignore'); -// #endregion Compatibility with old plugins section - -// eslint-disable-next-line no-unused-vars -const _jsFiles = ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs']; -const _tsFiles = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts']; -// recommended configuration only for ts files +const withFilesOnly = (config, files) => + config.map((e) => { + e.files = files; + return e; + }); +const _jsFiles = ['src/**/*.js', 'src/**/*.jsx', 'src/**/*.mjs', 'src/**/*.cjs']; +const _tsFiles = ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.mts', 'src/**/*.cts']; +const _codeFiles = [..._jsFiles, ..._tsFiles]; const config = eslintTs.config( + // Add all .gitignore files to the ignore path includeIgnoreFile(gitignorePath), + // Asure oackage-scripts as CJS, to avoid no-undef issues + { files: ['**/package-scripts.js'], languageOptions: { globals: globals.node } }, + // Recommended settings from ESLint for JS eslintJs.configs.recommended, + // Prettier plugin usage eslintPluginPrettierRecommended, - ...compat.extends( - 'plugin:import/recommended', - 'plugin:import/errors', - 'plugin:import/warnings', - 'plugin:import/typescript' + // Import default settings. Import is not yet ESLint 9 compatible + ...withFilesOnly( + compat.extends( + 'plugin:import/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript' + ), + _codeFiles ), + // Custom settings and rules for all JS and TS files { + files: _codeFiles, linterOptions: { reportUnusedDisableDirectives: true }, languageOptions: { + parser: eslintJs.parser, + parserOptions: eslintJs.parserOptions, globals: { ...globals.node }, @@ -65,27 +66,112 @@ const config = eslintTs.config( plugins: { 'no-null': eslintPluginNoNull, 'prefer-arrow': eslintPluginPreferArrow - // 'import': legacyPlugin("eslint-plugin-import", "import") + // import: legacyPlugin('eslint-plugin-import', 'import') + }, + settings: { + // 'import/ignore': ['i18next', 'fs', 'path'] }, rules: { + // Non plugin rules 'no-console': ['error'], - 'no-null/no-null': ['error'], + 'arrow-body-style': ['error'], + 'no-shadow': ['off'], + camelcase: ['error'], + 'capitalized-comments': ['off'], + 'default-case': ['error'], + 'dot-location': ['error', 'property'], + eqeqeq: ['error', 'smart'], + 'no-alert': ['error'], + 'no-bitwise': ['error'], + 'no-caller': ['error'], + 'no-constructor-return': ['error'], + 'no-div-regex': ['error'], + 'no-empty': ['error'], - // Keep it off until compatibility with v9 is achieved at the plugin - 'import/no-named-as-default': ['off'], - 'import/no-named-as-default-member': ['off'], - 'import/no-unresolved': ['off'], + 'no-empty-function': [ + 'error', + { + allow: ['constructors'] + } + ], - 'import/no-duplicates': ['error'], - /* - 'import/no-unresolved': [ + 'no-eval': ['error'], + 'no-extra-bind': ['error'], + 'no-import-assign': ['error'], + 'no-invalid-this': ['error'], + 'no-labels': ['error'], + 'no-multiple-empty-lines': ['error'], + 'no-new-wrappers': ['error'], + 'no-regex-spaces': ['error'], + 'no-return-assign': ['error'], + 'no-return-await': ['error'], + 'no-self-compare': ['error'], + 'no-throw-literal': ['error'], + 'no-undef-init': ['error'], + 'no-underscore-dangle': ['off'], + 'no-unused-expressions': ['error'], + 'no-useless-call': ['error'], + 'no-useless-concat': ['error'], + 'no-var': ['error'], + 'object-shorthand': ['error'], + 'one-var': ['error', 'never'], + 'prefer-const': ['error'], + 'prefer-regex-literals': ['error'], + radix: ['error'], + 'require-await': ['error'], + + 'sort-imports': [ + 'error', + { + ignoreCase: false, + ignoreDeclarationSort: true, + ignoreMemberSort: false, + memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], + allowSeparatedGroups: true + } + ], + + 'spaced-comment': ['error'], + 'use-isnan': ['error'], + 'valid-typeof': ['error'], + yoda: ['error'], + + // No Null plugin + 'no-null/no-null': ['error'], + + // Prefer arrow Plugin + 'prefer-arrow/prefer-arrow-functions': [ 'error', { - // ignore: ["^@?[\\w\\d-_]+/?[\\w\\d-_]+/[\\w\\d-_]+$"], + disallowPrototype: true, + singleReturnOnly: false, + classPropertiesAllowed: false } ], - */ + // Import is still not v9 compatible, some some rules fail. + // Instead of using the default provided configurations, + // we manually configure the rules, to avoid rules that have problems. + + // Helpful warnings + 'import/no-empty-named-blocks': ['error'], + 'import/no-extraneous-dependencies': ['error'], + 'import/no-mutable-exports': ['off'], + 'import/no-named-as-default': ['off'], + 'import/no-named-as-default-member': ['off'], + // Module systems + 'import/no-import-module-exports': ['error'], + // Static Analysis + 'import/default': ['off'], + 'import/namespace': ['off'], + 'import/no-absolute-path': ['error'], + 'import/no-dynamic-require': ['error'], + 'import/no-self-import': ['error'], + 'import/no-unresolved': ['off'], + 'import/no-useless-path-segments': ['error'], + 'import/no-webpack-loader-syntax': ['error'], + // Style guide + 'import/no-duplicates': ['error'], 'import/order': [ 'error', { @@ -100,22 +186,48 @@ const config = eslintTs.config( ] } }, - ...eslintTs.configs.strictTypeChecked.map((e) => { - e.files = _tsFiles; - return e; - }), + // This rules applies only for TS files, recommended typescript-eslint settings. + ...withFilesOnly(eslintTs.configs.strict, _tsFiles), + ...withFilesOnly(eslintTs.configs.stylistic, _tsFiles), + ...withFilesOnly(eslintTs.configs.strictTypeChecked, _tsFiles), + ...withFilesOnly(eslintTs.configs.stylisticTypeChecked, _tsFiles), + // Custom typescript-eslint configuration { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + files: _tsFiles, languageOptions: { parser: eslintTs.parser, parserOptions: { - project: ['./tsconfig.json'], + project: [tsConfigPath], impliedStrict: true, warnOnUnsupportedTypeScriptVersion: false } }, rules: { + '@typescript-eslint/prefer-nullish-coalescing': ['off'], + '@typescript-eslint/no-unnecessary-type-parameters': ['off'], '@typescript-eslint/no-unnecessary-condition': ['off'], + '@typescript-eslint/no-inferrable-types': ['off'], + '@typescript-eslint/no-namespace': ['off'], + '@typescript-eslint/prefer-regexp-exec': ['off'], + '@typescript-eslint/no-shadow': ['error'], + '@typescript-eslint/restrict-template-expressions': [ + 'error', + { + allowNumber: true + } + ], + '@typescript-eslint/explicit-member-accessibility': [ + 'error', + { + accessibility: 'explicit' + } + ], + '@typescript-eslint/member-ordering': [ + 'error', + { + default: ['signature', 'field', 'constructor', ['get', 'set'], 'method'] + } + ], '@typescript-eslint/no-unused-vars': [ 'error', { @@ -123,6 +235,14 @@ const config = eslintTs.config( caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_' } + ], + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { + allowExpressions: true, + allowTypedFunctionExpressions: true, + allowHigherOrderFunctions: true + } ] } } diff --git a/package-scripts.js b/package-scripts.js index 7f177fa..cb2b46c 100755 --- a/package-scripts.js +++ b/package-scripts.js @@ -1,4 +1,6 @@ -module.exports = { +const { tasks } = require('@gobstones/gobstones-scripts'); + +const defaultConfiguration = { options: { scripts: false, logLevel: 'warn', @@ -7,68 +9,116 @@ module.exports = { scripts: { default: { - script: 'nps help', + script: tasks.nps('help'), hiddenFromHelp: true }, test: { - script: 'nps lint', + script: tasks.nps('lint'), description: 'Run ESLint on all the files (src and tests)' }, build: { - script: 'nps clean.dist ' + '&& tsc', + script: tasks.serially( + tasks.nps('clean.dist'), + tasks.tsc({ emit: true }), + tasks.copy({ src: './static', dest: '../dist', cwd: './src' }) + ), description: 'Build the application into "dist" folder' }, + doc: { + script: tasks.serially( + tasks.nps('clean.docs'), + tasks.typedoc(), + tasks.copy({ src: './docs/index.html', dest: './docs/globals.html' }) + ), + description: 'Run Typedoc and generate docs', + watch: { + script: tasks.serially(tasks.nps('doc'), tasks.typedoc({ watch: true })), + description: 'Run Typedoc and generate docs and watch for changes.' + }, + serve: { + script: tasks.serially(tasks.nps('doc'), tasks.serve({ dir: './docs' })), + description: 'Run Typedoc and generate docs, then serve the docs as HTML', + watch: { + script: tasks.serially( + tasks.nps('doc'), + tasks.concurrently({ + typedoc: tasks.typedoc({ watch: true }), + serve: tasks.serve({ dir: './docs' }) + }) + ), + description: 'Run Typedoc and generate docs and watch for changes while serving the docs as HTML' + } + } + }, + clean: { - script: 'nps clean.dist && nps clean.docs', + script: tasks.serially(tasks.nps('clean.dist'), tasks.nps('clean.docs')), description: 'Remove all automatically generated files and folders', hiddenFromHelp: true, dist: { - script: 'rimraf ./dist', + script: tasks.remove({ files: './dist' }), description: 'Delete the dist folder', hiddenFromHelp: true + }, + docs: { + script: tasks.remove({ files: './docs' }), + description: 'Delete the docs folder', + hiddenFromHelp: true } }, lint: { - script: 'eslint --format stylish --color', + script: tasks.eslint(), description: 'Run ESLint on all the files (src and tests)', fix: { - script: 'eslint --format stylish --color --fix', + script: tasks.eslint({ fix: true }), description: 'Run ESLint on all the files (src and tests) with --fix option' } }, prettify: { - script: - 'prettier --no-error-on-unmatched-pattern --write + ' + - './{src,.github,.vscode,.husky,.}/{**,.}' + // folders - '/*.{js,jsx,cjs,mjs,ts,tsx,cts,json,json5,yml,yaml,md,markdown}', // extensions + script: tasks.serially( + tasks.prettify({ + files: './.husky/*[^_]' + }), + tasks.prettify({ + files: './{.github,.vscode,project-types,src,test}/{**,.}/*.{js,jsx,cjs,mjs,ts,tsx,mts,cts,yml,md,json,js}' + }), + tasks.prettify({ + files: '{.czrc,.editorconfig,.gitignore,.npmignore,.npmrc,.prettierrc}' + }), + tasks.prettify({ + files: './*.{js,jsx,cjs,mjs,ts,tsx,mts,cts,yml,md,json,js}' + }) + ), description: 'Run Prettier on all the files, writing the results' }, changelog: { - script: 'conventional-changelog -p angular -i CHANGELOG.md -s', - hiddenFromHelp: true, - description: 'Generate changelog based on tags', + script: tasks.npx('conventional-changelog -p angular -i CHANGELOG.md -s'), + description: 'Generate changelog based on commits', scratch: { - script: 'conventional-changelog -p angular -i CHANGELOG.md -s -r 0', + script: tasks.npx('conventional-changelog -p angular -i CHANGELOG.md -s -r 0'), description: 'Generate changelog based on tags, starting from scratch', hiddenFromHelp: true - } + }, + hiddenFromHelp: true }, license: { - script: 'license-check-and-add add -f ./license.config.json ', + script: tasks.license(), hiddenFromHelp: true, description: 'Add license information to all code files in the project', remove: { - script: 'license-check-and-add remove -f ./license.config.json ', + script: tasks.license('remove'), hiddenFromHelp: true, - description: 'Remove license information to all code files in the project' + description: 'Add license information to all code files in the project' } } } }; + +module.exports = defaultConfiguration; diff --git a/package.json b/package.json index 3f869ac..56b39c8 100755 --- a/package.json +++ b/package.json @@ -1,12 +1,8 @@ { "name": "@gobstones/typedoc-theme-gobstones", "description": "A simple theme for the Gobstones generated documentation.", - "version": "0.2.1", - "keywords": [ - "Gobstones", - "typedoc-theme", - "typedoc-theme-gobstones" - ], + "version": "0.3.0", + "keywords": ["Gobstones", "typedoc-theme", "typedoc-theme-gobstones"], "repository": { "type": "git", "url": "git+https://github.com/gobstones/typedoc-theme-gobstones.git" @@ -31,9 +27,7 @@ }, "typesVersions": { "*": { - "*": [ - "./dist/index.d.ts" - ] + "*": ["./dist/index.d.ts"] } }, "packageManager": "npm@10.8.0", @@ -41,40 +35,24 @@ "node": ">=18.0.0" }, "scripts": { - "prepare": "husky", + "prepare": "is-ci || husky", "prepack": "npm start build", - "start": "nps" + "start": "gobstones-scripts run", + "gbs": "gobstones-scripts" + }, + "config": { + "gobstones-scripts": { + "type": "Library", + "manager": "npm", + "use-local-tsconfig-json": true + } }, "peerDependencies": { "typedoc": "^0.26.4" }, "devDependencies": { - "@commitlint/cli": "^19.3.0", - "@commitlint/config-conventional": "^19.2.2", - "@eslint/compat": "^1.1.1", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.8.0", - "@types/node": "^22.0.2", - "commitizen": "^4.3.0", - "concurrently": "^8.2.2", - "conventional-changelog-cli": "^5.0.0", - "cz-conventional-changelog": "^3.3.0", - "eslint": "9.8.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-no-null": "^1.0.2", - "eslint-plugin-prefer-arrow": "^1.2.3", - "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-react-refresh": "^0.4.9", - "husky": "^9.1.4", - "is-ci": "^3.0.1", - "license-check-and-add": "^4.0.5", - "nps": "^5.10.0", - "prettier": "^3.3.3", - "rimraf": "^6.0.1", - "typedoc": "~0.26.4", - "typescript": "^5.5.4", - "typescript-eslint": "^8.0.1" + "@gobstones/gobstones-scripts": "^0.9.1", + "husky": "^9.1.4" }, "dependencies": { "typedoc-plugin-mdn-links": "^3.2.7", diff --git a/src/GobstonesThemeContext.tsx b/src/GobstonesThemeContext.tsx deleted file mode 100755 index 0a535ad..0000000 --- a/src/GobstonesThemeContext.tsx +++ /dev/null @@ -1,164 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) National University of Quilmes 2018-2024 - * Gobstones (TM) is a trademark of the National University of Quilmes. - * - * This program is free software distributed under the terms of the - * GNU Affero General Public License version 3. - * Additional terms added in compliance to section 7 of such license apply. - * - * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. - * ***************************************************************************** - */ -import { - DefaultThemeRenderContext, - PageEvent, - Reflection, - RenderTemplate, - JSX, - DocumentReflection, - ProjectReflection, - ContainerReflection, - DeclarationHierarchy, - DeclarationReflection, - ReferenceReflection, - SignatureReflection, - Type, - TypeParameterReflection, - DefaultTheme, - Options, - CommentDisplayPart -} from 'typedoc'; - -import { defaultLayout as _defaultLayout } from './layouts/default'; -// Comments -import { commentSummary as _commentSummary } from './partials/comments/commentSummary'; -import { commentTags as _commentTags } from './partials/comments/commentTags'; -import { reflectionFlags as _reflectionFlags } from './partials/comments/reflectionFlags'; -// Others -import { hierarchy as _hierarchy } from './partials/hierarchy'; -// Icons -import { buildRefIcons, icons } from './partials/icon'; -import { index as _index } from './partials/index'; -// Members -import { member as _member } from './partials/members/member'; -import { memberDeclaration as _memberDeclaration } from './partials/members/member.declaration'; -import { memberGetterSetter as _memberGetterSetter } from './partials/members/member.getterSetter'; -import { memberReference as _memberReference } from './partials/members/member.reference'; -import { memberSignatureBody as _memberSignatureBody } from './partials/members/member.signature.body'; -import { memberSignatureTitle as _memberSignatureTitle } from './partials/members/member.signature.title'; -import { memberSignatures as _memberSignatures } from './partials/members/member.signatures'; -import { memberSources as _memberSources } from './partials/members/member.sources'; -import { members as _members } from './partials/members/members'; -// Navigation -import { pageNavigation as _pageNavigation } from './partials/navigation/pageSidebar/pageNavigation'; -import { pageSidebar as _pageSidebar } from './partials/navigation/pageSidebar/pageSidebar'; -import { settings as _settings } from './partials/navigation/pageSidebar/settings'; -import { navigation as _navigation } from './partials/navigation/sidebar/navigation'; -import { sidebar as _sidebar } from './partials/navigation/sidebar/sidebar'; -import { sidebarLinks as _sidebarLinks } from './partials/navigation/sidebar/sidebarLinks'; -import { toolbar as _toolbar } from './partials/navigation/toolbar/toolbar'; -// Other -import { parameter as _parameter } from './partials/parameter'; -import { reflectionPreview as _reflectionPreview } from './partials/reflectionPreview'; -// Sections -import { breadcrumb as _breadcrumb } from './partials/sections/breadcrumb'; -import { footer as _footer } from './partials/sections/footer'; -import { header as _header } from './partials/sections/header'; -// Types -import { type as _type } from './partials/types/type'; -import { typeAndParent as _typeAndParent } from './partials/types/typeAndParent'; -import { typeParameters as _typeParameters } from './partials/types/typeParameters'; -// Templates -import { documentTemplate as _documentTemplate } from './templates/document'; -import { hierarchyTemplate as _hierarchyTemplate } from './templates/hierarchy'; -import { indexTemplate as _indexTemplate } from './templates/index'; -import { reflectionTemplate as _reflectionTemplate } from './templates/reflection'; - -function bind(fn: (f: F, ...a: L) => R, first: F) { - return (...r: L) => fn(first, ...r); -} - -export class GobstonesThemeContext extends DefaultThemeRenderContext { - private _customRefIcons: typeof icons; - - constructor( - readonly theme: DefaultTheme, - public page: PageEvent, - options: Options - ) { - super(theme, page, options); - this._customRefIcons = buildRefIcons(theme.icons, this); - } - - get icons(): Readonly { - return this._customRefIcons; - } - - // Markdown - override markdown = (md: readonly CommentDisplayPart[] | string | undefined) => { - const parsed = this.theme.markedPlugin.parseMarkdown(md || '', this.page, this); - return parsed; - }; - - // Layout - override defaultLayout: ( - template: RenderTemplate>, - props: PageEvent - ) => JSX.Element = bind(_defaultLayout, this); - - // Templates - override documentTemplate: (props: PageEvent) => JSX.Element = bind(_documentTemplate, this); - override hierarchyTemplate: (props: PageEvent) => JSX.Element = bind(_hierarchyTemplate, this); - override indexTemplate: (props: PageEvent) => JSX.Element = bind(_indexTemplate, this); - override reflectionTemplate: (props: PageEvent) => JSX.Element = bind( - _reflectionTemplate, - this - ); - - // Navigation - override toolbar: (props: PageEvent) => JSX.Element = bind(_toolbar, this); - override sidebar: (props: PageEvent) => JSX.Element = bind(_sidebar, this); - override pageSidebar: (props: PageEvent) => JSX.Element = bind(_pageSidebar, this); - override sidebarLinks: () => JSX.Element | null = bind(_sidebarLinks, this); - override settings: () => JSX.Element = bind(_settings, this); - override navigation: (props: PageEvent) => JSX.Element = bind(_navigation, this); - override pageNavigation: (props: PageEvent) => JSX.Element = bind(_pageNavigation, this); - - // Header and footer - override header: (props: PageEvent) => JSX.Element = bind(_header, this); - override breadcrumb: (props: Reflection) => JSX.Element | undefined = bind(_breadcrumb, this); - override footer: () => JSX.Element = bind(_footer, this); - - // Comments - override commentSummary: (props: Reflection) => JSX.Element | undefined = bind(_commentSummary, this); - override commentTags: (props: Reflection) => JSX.Element | undefined = bind(_commentTags, this); - override reflectionFlags: (props: Reflection) => JSX.Element = bind(_reflectionFlags, this); - - // Types - override type: (type: Type | undefined, options?: { topLevelLinks: boolean }) => JSX.Element = bind(_type, this); - override typeAndParent: (props: Type) => JSX.Element = bind(_typeAndParent, this); - override typeParameters: (typeParameters: TypeParameterReflection[]) => JSX.Element = bind(_typeParameters, this); - - // Members - override member: (props: DeclarationReflection | DocumentReflection) => JSX.Element = bind(_member, this); - override memberDeclaration: (props: DeclarationReflection) => JSX.Element = bind(_memberDeclaration, this); - override memberGetterSetter: (props: DeclarationReflection) => JSX.Element = bind(_memberGetterSetter, this); - override memberReference: (props: ReferenceReflection) => JSX.Element = bind(_memberReference, this); - override memberSignatureBody: (props: SignatureReflection) => JSX.Element = bind(_memberSignatureBody, this); - override memberSignatureTitle: (props: SignatureReflection) => JSX.Element = bind(_memberSignatureTitle, this); - override memberSignatures: (props: DeclarationReflection) => JSX.Element = bind(_memberSignatures, this); - override memberSources: (props: SignatureReflection | DeclarationReflection) => JSX.Element = bind( - _memberSources, - this - ); - override members: (props: ContainerReflection) => JSX.Element = bind(_members, this); - - // Others - override reflectionPreview: (props: Reflection) => JSX.Element | undefined = bind(_reflectionPreview, this); - - override hierarchy: (props: DeclarationHierarchy | undefined) => JSX.Element | undefined = bind(_hierarchy, this); - override index: (props: ContainerReflection) => JSX.Element = bind(_index, this); - - override parameter: (props: DeclarationReflection) => JSX.Element = bind(_parameter, this); -} diff --git a/src/plugins/MdnLinksPlugin.tsx b/src/Plugins/MdnLinksPlugin.tsx similarity index 73% rename from src/plugins/MdnLinksPlugin.tsx rename to src/Plugins/MdnLinksPlugin.tsx index 07cbe10..25ef8e5 100644 --- a/src/plugins/MdnLinksPlugin.tsx +++ b/src/Plugins/MdnLinksPlugin.tsx @@ -10,15 +10,24 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { Application } from 'typedoc'; + +/** + * @module Plugins + * @author Alan Rodas Bonjour + */ + // @ts-expect-error: Plugin is JS only, ignore import as any. import { load } from 'typedoc-plugin-mdn-links'; -import { TypedocPlugin } from '.'; +import { TypedocPlugin } from '../Utils/Plugins'; +/** + * A Plugin class that wraps the `typedoc-plugin-mdn-links` plugin. + */ export class MdnLinksPlugin extends TypedocPlugin { - public initialize(application: Application) { + /** @inheritdoc */ + public initialize(): void { // eslint-disable-next-line @typescript-eslint/no-unsafe-call - load(application); + load(this.application); } } diff --git a/src/plugins/MergeModulePlugin.tsx b/src/Plugins/MergeModulePlugin.tsx similarity index 70% rename from src/plugins/MergeModulePlugin.tsx rename to src/Plugins/MergeModulePlugin.tsx index 7721355..7f6e803 100644 --- a/src/plugins/MergeModulePlugin.tsx +++ b/src/Plugins/MergeModulePlugin.tsx @@ -10,13 +10,22 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { Application } from 'typedoc'; + +/** + * @module Plugins + * @author Alan Rodas Bonjour + */ + import { load } from 'typedoc-plugin-merge-modules'; -import { TypedocPlugin } from '.'; +import { TypedocPlugin } from '../Utils/Plugins'; +/** + * A Plugin class that wraps the `typedoc-plugin-merge-modules` plugin. + */ export class MergeModulePlugin extends TypedocPlugin { - public initialize(application: Application) { - load(application); + /** @inheritdoc */ + public initialize(): void { + load(this.application); } } diff --git a/src/plugins/NotExportedPlugin.tsx b/src/Plugins/NotExportedPlugin.tsx similarity index 72% rename from src/plugins/NotExportedPlugin.tsx rename to src/Plugins/NotExportedPlugin.tsx index 440b657..c1b45fa 100644 --- a/src/plugins/NotExportedPlugin.tsx +++ b/src/Plugins/NotExportedPlugin.tsx @@ -10,6 +10,12 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Plugins + * @author Alan Rodas Bonjour + */ + /** * Adapted copy of * typedoc-plugin-not-exported @@ -20,36 +26,59 @@ */ import { - Converter, - TypeScript, Application, + Converter, DeclarationReflection, - ReflectionKind, ParameterType, - Reflection + Reflection, + ReflectionKind, + TypeScript } from 'typedoc'; // version 0.20.16+ import { Context } from 'typedoc/dist/lib/converter/context'; -// eslint-disable-next-line import/namespace +// eslint-disable-next-line import/no-extraneous-dependencies import * as ts from 'typescript'; -import { TypedocPlugin } from '.'; +import { TypedocPlugin } from '../Utils/Plugins'; +// eslint-disable-next-line no-bitwise const ModuleFlags = TypeScript.SymbolFlags.ValueModule | TypeScript.SymbolFlags.NamespaceModule; +/** + * A Plugin class that re-defines the `typedoc-plugin-not-exported` plugin. + * + * @remarks + * As it was defined, the `typedoc-plugin-not-exported` plugin is not quite + * useful to our theme. By making small modifications, the plugin now uses + * the `internal` flag to also include elements as exported. + * + * Note that there are some limitations on the behavior of the plugin. The + * `internal` modifier should only be used with modules, or members of a module, + * but not classes or interfaces. + */ export class NotExportedPlugin extends TypedocPlugin { + /** + * The reflections already checked for module exports + */ private checkedForModuleExports = new Map>(); + + /** + * The tags to include into the documentation. Note that by default + * _internal_ is used, thus, requiring the includeInternal option to + * be set to true. + */ private _includedTags: `@${string}`[] = ['@internal']; - public initialize(application: Application) { - application.options.addDeclaration({ + /** @inheritdoc */ + public initialize(): void { + this.application.options.addDeclaration({ name: 'includeTags', defaultValue: this._includedTags, help: '[typedoc-theme-gobstones]: A set of tags used to add elements within it, even if not exported directly by the module.', type: ParameterType.Array }); - application.converter.on(Converter.EVENT_BEGIN, () => { - const includeTagTemp = application.options.getValue('includeTags'); + this.application.converter.on(Converter.EVENT_BEGIN, () => { + const includeTagTemp = this.application.options.getValue('includeTags'); if (typeof includeTagTemp === 'string') { const tagName = includeTagTemp.toLocaleLowerCase(); this._includedTags = (tagName.startsWith('@') ? [tagName] : [`@${tagName}`]) as `@${string}`[]; @@ -58,32 +87,33 @@ export class NotExportedPlugin extends TypedocPlugin { } }); - application.converter.on( + this.application.converter.on( Converter.EVENT_CREATE_DECLARATION, (context: Context, reflection: DeclarationReflection) => { this._lookForFakeExports(context, reflection); } ); - application.converter.on(Converter.EVENT_END, () => { + this.application.converter.on(Converter.EVENT_END, () => { this.checkedForModuleExports.clear(); }); // Fix for the new TypeDoc JSDoc tag linting. - application.on(Application.EVENT_BOOTSTRAP_END, () => { - const modifiers = application.options.getValue('modifierTags'); + this.application.on(Application.EVENT_BOOTSTRAP_END, () => { + const modifiers = this.application.options.getValue('modifierTags'); for (const tag of this._includedTags) { if (!modifiers.includes(tag)) { - application.options.setValue('modifierTags', [...modifiers, tag]); + this.application.options.setValue('modifierTags', [...modifiers, tag]); } } }); } - private _lookForFakeExports(context: Context, reflection: DeclarationReflection) { + private _lookForFakeExports(context: Context, reflection: DeclarationReflection): void { // Figure out where "not exports" will be placed, go up the tree until we get to // the module where it belongs. let targetModule = reflection; + // eslint-disable-next-line no-bitwise while (!targetModule.kindOf(ReflectionKind.Module | ReflectionKind.Project)) { targetModule = targetModule.parent as DeclarationReflection; } @@ -102,10 +132,11 @@ export class NotExportedPlugin extends TypedocPlugin { } } - private _checkFakeExportsOfFile(file: ts.SourceFile, context: Context) { + private _checkFakeExportsOfFile(file: ts.SourceFile, context: Context): void { const moduleSymbol = context.checker.getSymbolAtLocation(file); // Make sure we are allowed to call getExportsOfModule + // eslint-disable-next-line no-bitwise if (!moduleSymbol || (moduleSymbol.flags & ModuleFlags) === 0) { return; } diff --git a/src/plugins/RemoveReferencesPlugin.tsx b/src/Plugins/RemoveReferencesPlugin.tsx similarity index 73% rename from src/plugins/RemoveReferencesPlugin.tsx rename to src/Plugins/RemoveReferencesPlugin.tsx index 3c7cf9c..cd1545f 100644 --- a/src/plugins/RemoveReferencesPlugin.tsx +++ b/src/Plugins/RemoveReferencesPlugin.tsx @@ -10,15 +10,24 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { Application } from 'typedoc'; + +/** + * @module Plugins + * @author Alan Rodas Bonjour + */ + // @ts-expect-error: Plugin is JS only, ignore import as any. import { load } from 'typedoc-plugin-remove-references'; -import { TypedocPlugin } from '.'; +import { TypedocPlugin } from '../Utils/Plugins'; +/** + * A Plugin class that wraps the `typedoc-plugin-remove-references` plugin. + */ export class RemoveReferencesPlugin extends TypedocPlugin { - public initialize(application: Application) { + /** @inheritdoc */ + public initialize(): void { // eslint-disable-next-line @typescript-eslint/no-unsafe-call - load(application); + load(this.application); } } diff --git a/src/Plugins/index.ts b/src/Plugins/index.ts new file mode 100644 index 0000000..0e18881 --- /dev/null +++ b/src/Plugins/index.ts @@ -0,0 +1,32 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * This module includes a set of plugins that are going to + * be automatically loaded when using the gobstones theme. + * + * @remarks + * Most of these are third-party plugins that are just wrapped + * inside a custom class, as to provide a simple mechanism + * for loading them. + * + * @module Plugins + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './MdnLinksPlugin'; +export * from './MergeModulePlugin'; +export * from './NotExportedPlugin'; +export * from './RemoveReferencesPlugin'; diff --git a/src/GobstonesTheme.tsx b/src/Theme/GobstonesTheme.tsx similarity index 66% rename from src/GobstonesTheme.tsx rename to src/Theme/GobstonesTheme.tsx index 0790424..5e6bd33 100755 --- a/src/GobstonesTheme.tsx +++ b/src/Theme/GobstonesTheme.tsx @@ -10,12 +10,19 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme + * @author Alan Rodas Bonjour + */ + import fs from 'fs'; import path from 'path'; import { DeclarationReflection, DefaultTheme, + DefaultThemeRenderContext, DocumentReflection, PageEvent, Reflection, @@ -25,14 +32,33 @@ import { import { GobstonesThemeContext } from './GobstonesThemeContext'; +/** + * This class represents the main element of the theme. + * An instance of this class is automatically created by + * TypeDoc when the theme is going to be used, and it's + * `initialize` method called upon theme initialization. + * + * @remarks + * A Theme can be defined as a subclass of the built-in TypeDoc's + * `Theme` class. Yet, it is most useful to re-use basic behavior by + * extending the `DefaultTheme` class instead. + * By extending this class, we should only overwrite methods that are + * going to change from the default class, most likely the {@link initialize} + * method and the {@link getRenderContext} methods. By doing this we can also + * have the benefit of accessing the currently running application through the + * `this.application` property, allowing us to access different hooks. + */ export class GobstonesTheme extends DefaultTheme { - initialize() { - // The initialize method is called upon theme setting, - // it allows to hook up at different places to perform some actions. + /** + * The initialize method is called upon theme setup by TypeDoc. + * This method's main purpose is to provide basic theme setup, + * either at startup or at specific moments by using the application + * hooks. + */ + public initialize(): void { super.initialize(); - const staticResourceFolder = path.resolve(__dirname, path.join('..', 'src', 'static')); - + const staticResourceFolder = path.resolve(__dirname, path.join('..', 'static')); // Triggered when all elements are ready at output folder this.application.renderer.on(RendererEvent.END, () => { // We are going to reorganize assets se they live in a proper @@ -68,16 +94,36 @@ export class GobstonesTheme extends DefaultTheme { // All assets are now copied over the destination folder } - getRenderContext(pageEvent: PageEvent) { + /** + * Returns the render context for this theme. + * + * @param pageEvent - A page event with a reflection. + * + * @returns The associated render context for this theme. + */ + public getRenderContext(pageEvent: PageEvent): DefaultThemeRenderContext { // The render controls the different components and files this // theme is going to use. return new GobstonesThemeContext(this, pageEvent, this.application.options); } - getReflectionClasses(reflection: DeclarationReflection | DocumentReflection) { - function toStyleClass(str: string): string { - return str.replace(/(\w)([A-Z])/g, (_m, m1: string, m2: string) => m1 + '-' + m2).toLowerCase(); - } + /** + * Get the reflection classes of this theme. + * + * @remarks + * Reflection classes are used at the theme's representation when + * presented into the interface. This being here doesn't seem to make + * much sense, as it has to do with the rendering and not with the + * theme itself. Yet, this is how it's defined in the original theme, + * thus, we need to overwrite it here. + * + * @param reflection - The reflection to get the classes from. + * + * @returns A string that represents all reflection classes. + */ + public getReflectionClasses(reflection: DeclarationReflection | DocumentReflection): string { + const toStyleClass = (str: string): string => + str.replace(/(\w)([A-Z])/g, (_m, m1: string, m2: string) => m1 + '-' + m2).toLowerCase(); const filters = this.application.options.getValue('visibilityFilters') as Record; @@ -118,8 +164,8 @@ export class GobstonesTheme extends DefaultTheme { } else if ( reflection.comment?.hasModifier(key as `@${string}`) || reflection.comment?.getTag(key as `@${string}`) || - (firstSignature && firstSignature.comment?.hasModifier(key as `@${string}`)) || - (firstSignature && firstSignature.comment?.getTag(key as `@${string}`)) + firstSignature?.comment?.hasModifier(key as `@${string}`) || + firstSignature?.comment?.getTag(key as `@${string}`) ) { classes.push(toStyleClass(`tsd-is-${key.substring(1)}`)); } diff --git a/src/Theme/GobstonesThemeContext.tsx b/src/Theme/GobstonesThemeContext.tsx new file mode 100755 index 0000000..83fbe05 --- /dev/null +++ b/src/Theme/GobstonesThemeContext.tsx @@ -0,0 +1,200 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * @module Theme + * @author Alan Rodas Bonjour + */ + +import { + CommentDisplayPart, + ContainerReflection, + DeclarationHierarchy, + DeclarationReflection, + DefaultTheme, + DefaultThemeRenderContext, + DocumentReflection, + JSX, + Options, + PageEvent, + ProjectReflection, + ReferenceReflection, + Reflection, + RenderTemplate, + SignatureReflection, + Type, + TypeParameterReflection +} from 'typedoc'; + +import { buildRefIcons, icons } from './Icons'; +import { defaultLayout as _defaultLayout } from './Layouts'; +import { + breadcrumb as _breadcrumb, + commentSummary as _commentSummary, + commentTags as _commentTags, + footer as _footer, + header as _header, + hierarchy as _hierarchy, + index as _index, + member as _member, + memberDeclaration as _memberDeclaration, + memberGetterSetter as _memberGetterSetter, + memberReference as _memberReference, + memberSignatureBody as _memberSignatureBody, + memberSignatureTitle as _memberSignatureTitle, + memberSignatures as _memberSignatures, + memberSources as _memberSources, + members as _members, + navigation as _navigation, + pageNavigation as _pageNavigation, + pageSidebar as _pageSidebar, + parameter as _parameter, + reflectionFlags as _reflectionFlags, + reflectionPreview as _reflectionPreview, + settings as _settings, + sidebar as _sidebar, + sidebarLinks as _sidebarLinks, + toolbar as _toolbar, + type as _type, + typeAndParent as _typeAndParent, + typeParameters as _typeParameters +} from './Partials'; +import { + documentTemplate as _documentTemplate, + hierarchyTemplate as _hierarchyTemplate, + indexTemplate as _indexTemplate, + reflectionTemplate as _reflectionTemplate +} from './Templates'; + +/** + * Return a partially applied version of the given function, such that + * the first argument will be the given `first` element. + * + * @param fn - The function to partially apply. + * @param first - The first argument to apply. + * + * @returns A partially applied function + */ +const bind = + (fn: (f: F, ...a: L) => R, first: F) => + (...r: L) => + fn(first, ...r); + +/** + * The main context of the theme. This class contains the + * basic behavior of how the theme should look on different scenarios, + * that is, how it will render every part of the theme depending on + * the reflection to render. + */ +export class GobstonesThemeContext extends DefaultThemeRenderContext { + private _customRefIcons: typeof icons; + + // Layout + public override defaultLayout: ( + template: RenderTemplate>, + props: PageEvent + ) => JSX.Element = bind(_defaultLayout, this); + + // Templates + public override documentTemplate: (props: PageEvent) => JSX.Element = bind( + _documentTemplate, + this + ); + public override hierarchyTemplate: (props: PageEvent) => JSX.Element = bind( + _hierarchyTemplate, + this + ); + public override indexTemplate: (props: PageEvent) => JSX.Element = bind(_indexTemplate, this); + public override reflectionTemplate: (props: PageEvent) => JSX.Element = bind( + _reflectionTemplate, + this + ); + + // Navigation + public override toolbar: (props: PageEvent) => JSX.Element = bind(_toolbar, this); + public override sidebar: (props: PageEvent) => JSX.Element = bind(_sidebar, this); + public override pageSidebar: (props: PageEvent) => JSX.Element = bind(_pageSidebar, this); + public override sidebarLinks: () => JSX.Element | null = bind(_sidebarLinks, this); + public override settings: () => JSX.Element = bind(_settings, this); + public override navigation: (props: PageEvent) => JSX.Element = bind(_navigation, this); + public override pageNavigation: (props: PageEvent) => JSX.Element = bind(_pageNavigation, this); + + // Header and footer + public override header: (props: PageEvent) => JSX.Element = bind(_header, this); + public override breadcrumb: (props: Reflection) => JSX.Element | undefined = bind(_breadcrumb, this); + public override footer: () => JSX.Element = bind(_footer, this); + + // Comments + public override commentSummary: (props: Reflection) => JSX.Element | undefined = bind(_commentSummary, this); + public override commentTags: (props: Reflection) => JSX.Element | undefined = bind(_commentTags, this); + public override reflectionFlags: (props: Reflection) => JSX.Element = bind(_reflectionFlags, this); + + // Types + public override type: (type: Type | undefined, options?: { topLevelLinks: boolean }) => JSX.Element = bind( + _type, + this + ); + public override typeAndParent: (props: Type) => JSX.Element = bind(_typeAndParent, this); + public override typeParameters: (typeParameters: TypeParameterReflection[]) => JSX.Element = bind( + _typeParameters, + this + ); + + // Members + public override member: (props: DeclarationReflection | DocumentReflection) => JSX.Element = bind(_member, this); + public override memberDeclaration: (props: DeclarationReflection) => JSX.Element = bind(_memberDeclaration, this); + public override memberGetterSetter: (props: DeclarationReflection) => JSX.Element = bind(_memberGetterSetter, this); + public override memberReference: (props: ReferenceReflection) => JSX.Element = bind(_memberReference, this); + public override memberSignatureBody: (props: SignatureReflection) => JSX.Element = bind(_memberSignatureBody, this); + public override memberSignatureTitle: (props: SignatureReflection) => JSX.Element = bind( + _memberSignatureTitle, + this + ); + public override memberSignatures: (props: DeclarationReflection) => JSX.Element = bind(_memberSignatures, this); + public override memberSources: (props: SignatureReflection | DeclarationReflection) => JSX.Element = bind( + _memberSources, + this + ); + + public override members: (props: ContainerReflection) => JSX.Element = bind(_members, this); + + // Others + public override reflectionPreview: (props: Reflection) => JSX.Element | undefined = bind(_reflectionPreview, this); + + public override hierarchy: (props: DeclarationHierarchy | undefined) => JSX.Element | undefined = bind( + _hierarchy, + this + ); + public override index: (props: ContainerReflection) => JSX.Element = bind(_index, this); + + public override parameter: (props: DeclarationReflection) => JSX.Element = bind(_parameter, this); + + public constructor( + public readonly theme: DefaultTheme, + public page: PageEvent, + options: Options + ) { + super(theme, page, options); + this._customRefIcons = buildRefIcons(theme.icons, this); + } + + public get icons(): Readonly { + return this._customRefIcons; + } + + // Markdown + public override markdown = (md: readonly CommentDisplayPart[] | string | undefined): string => { + const parsed = this.theme.markedPlugin.parseMarkdown(md || '', this.page, this); + return parsed; + }; +} diff --git a/src/partials/icon.tsx b/src/Theme/Icons/icon.tsx similarity index 92% rename from src/partials/icon.tsx rename to src/Theme/Icons/icon.tsx index 48a4afe..625673e 100755 --- a/src/partials/icon.tsx +++ b/src/Theme/Icons/icon.tsx @@ -10,12 +10,28 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Icons + * @author Alan Rodas Bonjour + */ import assert from 'assert'; -import { ReflectionKind, JSX } from 'typedoc'; +import { JSX, ReflectionKind } from 'typedoc'; import type { DefaultThemeRenderContext } from 'typedoc'; -const kindIcon = (letterPath: JSX.Element, color: string, circular = false) => ( +/** + * Return an element that contains the SVG for the icon used by the kind of + * an element, such as the icon to use for classes, the one used by functions + * and so on. + * + * @param letterPath - The svg element that represents the letter to showcase inside the icon. + * @param color - The color to use as outline of this icon. + * @param circular - Whether to display this as a circular icon (if not, squared is default). + * + * @returns An SVG.Element with the SVG icon. + */ +const kindIcon = (letterPath: JSX.Element, color: string, circular = false): JSX.Element => ( ( ); -export function buildRefIcons JSX.Element>>( +/** + * Returns a record of all possible icon names with a function that returns + * the matching JSX.Element for such icon. + * + * @param icons - The icons + * @param context - The render context of this theme + * @returns A function that creates all icons. + */ +export const buildRefIcons = JSX.Element>>( icons: T, context: DefaultThemeRenderContext -): T { +): T => { const refs: Record JSX.Element> = {}; for (const [name, builder] of Object.entries(icons)) { @@ -56,8 +80,12 @@ export function buildRefIcons JSX.Element>>( } return refs as T; -} +}; +/** + * A record of all possible reflection kinds along with a function that + * returns it's associated icon. + */ export const icons: Record< ReflectionKind | 'chevronDown' | 'checkbox' | 'menu' | 'search' | 'chevronSmall' | 'anchor', () => JSX.Element @@ -133,15 +161,14 @@ export const icons: Record< '#FF4DB8', true ), - [ReflectionKind.Module]() { - return kindIcon( + [ReflectionKind.Module]: () => + kindIcon( , 'var(--color-ts-module)' - ); - }, + ), [ReflectionKind.Namespace]: () => kindIcon( + * + * @internal + */ -export function loadPlugin(application: Application, plugin: new () => TypedocPlugin) { - new plugin().initialize(application); -} +export * from './icon'; diff --git a/src/layouts/default.tsx b/src/Theme/Layouts/default.tsx similarity index 81% rename from src/layouts/default.tsx rename to src/Theme/Layouts/default.tsx index a7d1721..f202c78 100755 --- a/src/layouts/default.tsx +++ b/src/Theme/Layouts/default.tsx @@ -10,30 +10,64 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Layouts + * @author Alan Rodas Bonjour + */ + import { - RenderTemplate, - Reflection, DeclarationReflection, - ProjectReflection, + DefaultThemeRenderContext, JSX, PageEvent, - DefaultThemeRenderContext + ProjectReflection, + Reflection, + RenderTemplate } from 'typedoc'; -export function getDisplayName(refl: Reflection): string { +/** + * The name to display as the main title of the documentation. + * Default to the package name followed by a dash and a "v" followd + * by the version number. + * + * @param refl - The main reflection + * + * @returns The display name as a string + */ +export const getDisplayName = (refl: Reflection): string => { let version = ''; if ((refl instanceof DeclarationReflection || refl instanceof ProjectReflection) && refl.packageVersion) { version = ` - v${refl.packageVersion}`; } return `${refl.name}${version}`; -} +}; +/** + * The default layout to use by the theme. + * + * @remarks + * The layout is the main structure of the produced HTML document, including + * the main tags, such as head and body, as well as loading all the scripts + * and styles. + * + * @privateRemarks + * Note that there are ways to load a style or script into a theme through code, + * yet, as we are redefining the template, loading custom elements is done entirely + * into this file. + * + * @param context - The theme's context + * @param template - The template in use. + * @param props - The page event with the reflection. + * + * @returns The default layout in use as a JSX.Element. + */ export const defaultLayout = ( context: DefaultThemeRenderContext, template: RenderTemplate>, props: PageEvent -) => ( +): JSX.Element => ( diff --git a/src/partials/navigation/sidebar/sidebar.tsx b/src/Theme/Layouts/index.ts similarity index 66% rename from src/partials/navigation/sidebar/sidebar.tsx rename to src/Theme/Layouts/index.ts index 7ee6193..23d5d04 100644 --- a/src/partials/navigation/sidebar/sidebar.tsx +++ b/src/Theme/Layouts/index.ts @@ -10,13 +10,15 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { Reflection, JSX, PageEvent, DefaultThemeRenderContext } from 'typedoc'; -export function sidebar(context: DefaultThemeRenderContext, props: PageEvent) { - return ( - <> - {context.sidebarLinks()} - {context.navigation(props)} - - ); -} +/** + * This module contains the main layouts of the theme + * and all it's important elements. A layout comprises the main + * structure of the exported HTML document. + * + * @module Theme/Layouts + * @author Alan Rodas Bonjour + * + * @internal + */ +export * from './default'; diff --git a/src/partials/comments/commentSummary.tsx b/src/Theme/Partials/Comments/commentSummary.tsx similarity index 75% rename from src/partials/comments/commentSummary.tsx rename to src/Theme/Partials/Comments/commentSummary.tsx index 66e4f96..76c8299 100644 --- a/src/partials/comments/commentSummary.tsx +++ b/src/Theme/Partials/Comments/commentSummary.tsx @@ -10,11 +10,17 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { JSX, DefaultThemeRenderContext, Reflection } from 'typedoc'; + +/** + * @module Theme/Partials/Comments + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, Reflection } from 'typedoc'; // Note: Comment modifiers are handled in `renderFlags` -export function commentSummary({ markdown }: DefaultThemeRenderContext, props: Reflection) { +export const commentSummary = ({ markdown }: DefaultThemeRenderContext, props: Reflection): JSX.Element | undefined => { if (!props.comment?.summary.some((part) => part.text)) return; return ( @@ -22,4 +28,4 @@ export function commentSummary({ markdown }: DefaultThemeRenderContext, props: R ); -} +}; diff --git a/src/partials/comments/commentTags.tsx b/src/Theme/Partials/Comments/commentTags.tsx similarity index 88% rename from src/partials/comments/commentTags.tsx rename to src/Theme/Partials/Comments/commentTags.tsx index 9ec2ff7..80b4090 100644 --- a/src/partials/comments/commentTags.tsx +++ b/src/Theme/Partials/Comments/commentTags.tsx @@ -10,11 +10,17 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { JSX, DefaultThemeRenderContext, Reflection, ReflectionKind } from 'typedoc'; + +/** + * @module Theme/Partials/Comments + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, Reflection, ReflectionKind } from 'typedoc'; import { anchorIcon } from '../anchor-icon'; -export function commentTags(context: DefaultThemeRenderContext, props: Reflection) { +export const commentTags = (context: DefaultThemeRenderContext, props: Reflection): JSX.Element | undefined => { if (!props.comment) return; const beforeTags = context.hook('comment.beforeTags', context, props.comment, props); @@ -50,4 +56,4 @@ export function commentTags(context: DefaultThemeRenderContext, props: Reflectio {afterTags} ); -} +}; diff --git a/src/partials/navigation/pageSidebar/pageSidebar.tsx b/src/Theme/Partials/Comments/index.ts similarity index 66% rename from src/partials/navigation/pageSidebar/pageSidebar.tsx rename to src/Theme/Partials/Comments/index.ts index 12eeb6d..2f24f73 100644 --- a/src/partials/navigation/pageSidebar/pageSidebar.tsx +++ b/src/Theme/Partials/Comments/index.ts @@ -10,13 +10,15 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { Reflection, JSX, PageEvent, DefaultThemeRenderContext } from 'typedoc'; +/** + * This module contains the components that are used when rendering comments. + * + * @module Theme/Partials/Comments + * @author Alan Rodas Bonjour + * + * @internal + */ -export function pageSidebar(context: DefaultThemeRenderContext, props: PageEvent) { - return ( - <> - {context.settings()} - {context.pageNavigation(props)} - - ); -} +export * from './commentSummary'; +export * from './commentTags'; +export * from './reflectionFlags'; diff --git a/src/partials/comments/reflectionFlags.tsx b/src/Theme/Partials/Comments/reflectionFlags.tsx similarity index 79% rename from src/partials/comments/reflectionFlags.tsx rename to src/Theme/Partials/Comments/reflectionFlags.tsx index 40ea191..6bce67e 100644 --- a/src/partials/comments/reflectionFlags.tsx +++ b/src/Theme/Partials/Comments/reflectionFlags.tsx @@ -10,13 +10,22 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { JSX, DefaultThemeRenderContext, Reflection } from 'typedoc'; -import { join } from '../lib'; +/** + * @module Theme/Partials/Comments + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, Reflection } from 'typedoc'; +import { join } from '../../../Utils/lib'; + +/** + * A + */ const flagsNotRendered: `@${string}`[] = ['@showCategories', '@showGroups', '@hideCategories', '@hideGroups']; -export function reflectionFlags(context: DefaultThemeRenderContext, props: Reflection) { +export const reflectionFlags = (context: DefaultThemeRenderContext, props: Reflection): JSX.Element => { const allFlags = props.flags.getFlagStrings(context.internationalization); if (props.comment) { for (const tag of props.comment.modifierTags) { @@ -29,4 +38,4 @@ export function reflectionFlags(context: DefaultThemeRenderContext, props: Refle return join(' ', allFlags as unknown as JSX.Element[], (item: JSX.Element) => ( {item} )); -} +}; diff --git a/src/Theme/Partials/Members/index.ts b/src/Theme/Partials/Members/index.ts new file mode 100644 index 0000000..9c3042f --- /dev/null +++ b/src/Theme/Partials/Members/index.ts @@ -0,0 +1,30 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module contains the components that are used when rendering different members. + * + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './member'; +export * from './members'; +export * from './member.declaration'; +export * from './member.getterSetter'; +export * from './member.reference'; +export * from './member.signatures'; +export * from './member.signature.body'; +export * from './member.signature.title'; +export * from './member.sources'; diff --git a/src/partials/members/member.declaration.tsx b/src/Theme/Partials/Members/member.declaration.tsx similarity index 83% rename from src/partials/members/member.declaration.tsx rename to src/Theme/Partials/Members/member.declaration.tsx index 4e94526..6731693 100755 --- a/src/partials/members/member.declaration.tsx +++ b/src/Theme/Partials/Members/member.declaration.tsx @@ -10,25 +10,31 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; -import type { DeclarationReflection, ReflectionType, DefaultThemeRenderContext } from 'typedoc'; +import type { DeclarationReflection, DefaultThemeRenderContext, ReflectionType } from 'typedoc'; -import { getKindClass, hasTypeParameters, renderTypeParametersSignature, wbr } from '../lib'; +import { getKindClass, hasTypeParameters, renderTypeParametersSignature, wbr } from '../../../Utils/lib'; -function renderingTypeDeclarationIsUseful(declaration: DeclarationReflection): boolean { +const renderingTypeDeclarationIsUseful = (declaration: DeclarationReflection): boolean => { if (declaration.hasComment()) return true; if (declaration.children?.some(renderingTypeDeclarationIsUseful)) return true; if (declaration.type?.type === 'reflection' && renderingTypeDeclarationIsUseful(declaration.type.declaration)) { return true; } - return declaration.getAllSignatures().some((sig) => { - return sig.hasComment() || sig.parameters?.some((p) => p.hasComment()); - }); -} + return declaration + .getAllSignatures() + .some((sig) => sig.hasComment() || sig.parameters?.some((p) => p.hasComment())); +}; -export function memberDeclaration(context: DefaultThemeRenderContext, props: DeclarationReflection) { - function renderTypeDeclaration(type: ReflectionType) { +export const memberDeclaration = (context: DefaultThemeRenderContext, props: DeclarationReflection): JSX.Element => { + const renderTypeDeclaration = (type: ReflectionType): JSX.Element | undefined => { if (renderingTypeDeclarationIsUseful(type.declaration)) { return (
@@ -38,7 +44,7 @@ export function memberDeclaration(context: DefaultThemeRenderContext, props: Dec ); } return undefined; - } + }; const visitor = { reflection: renderTypeDeclaration }; @@ -96,4 +102,4 @@ export function memberDeclaration(context: DefaultThemeRenderContext, props: Dec {context.memberSources(props)} ); -} +}; diff --git a/src/partials/members/member.getterSetter.tsx b/src/Theme/Partials/Members/member.getterSetter.tsx similarity index 91% rename from src/partials/members/member.getterSetter.tsx rename to src/Theme/Partials/Members/member.getterSetter.tsx index 9871c64..0f0d4f3 100755 --- a/src/partials/members/member.getterSetter.tsx +++ b/src/Theme/Partials/Members/member.getterSetter.tsx @@ -10,12 +10,18 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; import type { DeclarationReflection, DefaultThemeRenderContext } from 'typedoc'; -import { classNames } from '../lib'; +import { classNames } from '../../../Utils/lib'; -export const memberGetterSetter = (context: DefaultThemeRenderContext, props: DeclarationReflection) => ( +export const memberGetterSetter = (context: DefaultThemeRenderContext, props: DeclarationReflection): JSX.Element => ( <>
+ */ + import { JSX } from 'typedoc'; import type { DefaultThemeRenderContext, ReferenceReflection } from 'typedoc'; export const memberReference = ( { urlTo, i18n, commentSummary, commentTags }: DefaultThemeRenderContext, props: ReferenceReflection -) => { +): JSX.Element => { const referenced = props.tryGetTargetReflectionDeep(); if (!referenced) { diff --git a/src/partials/members/member.signature.body.tsx b/src/Theme/Partials/Members/member.signature.body.tsx similarity index 91% rename from src/partials/members/member.signature.body.tsx rename to src/Theme/Partials/Members/member.signature.body.tsx index 29e86da..1c00d1d 100755 --- a/src/partials/members/member.signature.body.tsx +++ b/src/Theme/Partials/Members/member.signature.body.tsx @@ -10,16 +10,22 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + */ + /* eslint-disable no-null/no-null */ -import { DefaultThemeRenderContext, ReflectionType, SignatureReflection, JSX } from 'typedoc'; +import { DefaultThemeRenderContext, JSX, ReflectionType, SignatureReflection } from 'typedoc'; -import { hasTypeParameters } from '../lib'; +import { hasTypeParameters } from '../../../Utils/lib'; -export function memberSignatureBody( +export const memberSignatureBody = ( context: DefaultThemeRenderContext, props: SignatureReflection, { hideSources = false }: { hideSources?: boolean } = {} -) { +): JSX.Element => { const returnsTag = props.comment?.getTag('@returns'); return ( @@ -71,4 +77,4 @@ export function memberSignatureBody( {!hideSources && context.memberSources(props)} ); -} +}; diff --git a/src/partials/members/member.signature.title.tsx b/src/Theme/Partials/Members/member.signature.title.tsx similarity index 58% rename from src/partials/members/member.signature.title.tsx rename to src/Theme/Partials/Members/member.signature.title.tsx index ec70520..5907fc3 100755 --- a/src/partials/members/member.signature.title.tsx +++ b/src/Theme/Partials/Members/member.signature.title.tsx @@ -10,36 +10,38 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { DefaultThemeRenderContext, ParameterReflection, ReflectionKind, SignatureReflection, JSX } from 'typedoc'; -import { getKindClass, join, renderTypeParametersSignature, wbr } from '../lib'; +/** + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + */ -function renderParameterWithType(context: DefaultThemeRenderContext, item: ParameterReflection) { - return ( - <> - {!!item.flags.isRest && ...} - {item.name} - - {!!item.flags.isOptional && '?'} - {!!item.defaultValue && '?'} - {': '} - - {context.type(item.type)} - - ); -} +import { DefaultThemeRenderContext, JSX, ParameterReflection, ReflectionKind, SignatureReflection } from 'typedoc'; -function renderParameterWithoutType(item: ParameterReflection) { - return ( - <> - {!!item.flags.isRest && ...} - {item.name} - {(item.flags.isOptional || item.defaultValue) && ?} - - ); -} +import { getKindClass, join, renderTypeParametersSignature, wbr } from '../../../Utils/lib'; + +const renderParameterWithType = (context: DefaultThemeRenderContext, item: ParameterReflection): JSX.Element => ( + <> + {!!item.flags.isRest && ...} + {item.name} + + {!!item.flags.isOptional && '?'} + {!!item.defaultValue && '?'} + {': '} + + {context.type(item.type)} + +); + +const renderParameterWithoutType = (item: ParameterReflection): JSX.Element => ( + <> + {!!item.flags.isRest && ...} + {item.name} + {(item.flags.isOptional || item.defaultValue) && ?} + +); -export function memberSignatureTitle( +export const memberSignatureTitle = ( context: DefaultThemeRenderContext, props: SignatureReflection, { @@ -47,9 +49,11 @@ export function memberSignatureTitle( arrowStyle = false, hideParamTypes = context.options.getValue('hideParameterTypesInTitle') }: { hideName?: boolean; arrowStyle?: boolean; hideParamTypes?: boolean } = {} -) { - // eslint-disable-next-line no-null/no-null - const renderParam = hideParamTypes ? renderParameterWithoutType : renderParameterWithType.bind(null, context); +): JSX.Element => { + const renderParam: (item: ParameterReflection) => JSX.Element = hideParamTypes + ? renderParameterWithoutType + : // eslint-disable-next-line no-null/no-null + (renderParameterWithType.bind(null, context) as (item: ParameterReflection) => JSX.Element); return ( <> @@ -77,4 +81,4 @@ export function memberSignatureTitle( )} ); -} +}; diff --git a/src/partials/members/member.signatures.tsx b/src/Theme/Partials/Members/member.signatures.tsx similarity index 83% rename from src/partials/members/member.signatures.tsx rename to src/Theme/Partials/Members/member.signatures.tsx index 77b8016..a7e7173 100755 --- a/src/partials/members/member.signatures.tsx +++ b/src/Theme/Partials/Members/member.signatures.tsx @@ -10,13 +10,19 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; -import type { DefaultThemeRenderContext, DeclarationReflection } from 'typedoc'; +import type { DeclarationReflection, DefaultThemeRenderContext } from 'typedoc'; +import { classNames } from '../../../Utils/lib'; import { anchorIcon } from '../anchor-icon'; -import { classNames } from '../lib'; -export const memberSignatures = (context: DefaultThemeRenderContext, props: DeclarationReflection) => ( +export const memberSignatures = (context: DefaultThemeRenderContext, props: DeclarationReflection): JSX.Element => ( <>
{props.signatures?.map((item) => ( diff --git a/src/partials/members/member.sources.tsx b/src/Theme/Partials/Members/member.sources.tsx similarity index 89% rename from src/partials/members/member.sources.tsx rename to src/Theme/Partials/Members/member.sources.tsx index bb8aba7..c7110cd 100755 --- a/src/partials/members/member.sources.tsx +++ b/src/Theme/Partials/Members/member.sources.tsx @@ -10,10 +10,16 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; -import type { DefaultThemeRenderContext, DeclarationReflection, SignatureReflection, SourceReference } from 'typedoc'; +import type { DeclarationReflection, DefaultThemeRenderContext, SignatureReflection, SourceReference } from 'typedoc'; -function sourceLink(context: DefaultThemeRenderContext, item: SourceReference) { +const sourceLink = (context: DefaultThemeRenderContext, item: SourceReference): JSX.Element => { if (!item.url) { return (
  • @@ -41,12 +47,12 @@ function sourceLink(context: DefaultThemeRenderContext, item: SourceReference) {
  • ); -} +}; export const memberSources = ( context: DefaultThemeRenderContext, props: SignatureReflection | DeclarationReflection -) => { +): JSX.Element => { const sources: JSX.Element[] = []; if (props.implementationOf) { diff --git a/src/partials/members/member.tsx b/src/Theme/Partials/Members/member.tsx similarity index 87% rename from src/partials/members/member.tsx rename to src/Theme/Partials/Members/member.tsx index e2b7b26..fb52342 100755 --- a/src/partials/members/member.tsx +++ b/src/Theme/Partials/Members/member.tsx @@ -10,18 +10,27 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + */ + import { - DefaultThemeRenderContext, DeclarationReflection, + DefaultThemeRenderContext, DocumentReflection, - ReferenceReflection, - JSX + JSX, + ReferenceReflection } from 'typedoc'; +import { classNames, getDisplayName, wbr } from '../../../Utils/lib'; import { anchorIcon } from '../anchor-icon'; -import { classNames, getDisplayName, wbr } from '../lib'; -export function member(context: DefaultThemeRenderContext, props: DeclarationReflection | DocumentReflection) { +export const member = ( + context: DefaultThemeRenderContext, + props: DeclarationReflection | DocumentReflection +): JSX.Element => { context.page.pageHeadings.push({ link: `#${props.anchor ?? ''}`, text: getDisplayName(props), @@ -71,7 +80,7 @@ export function member(context: DefaultThemeRenderContext, props: DeclarationRef ? context.memberReference(props) : context.memberDeclaration(props)} - {props.groups?.map((item) => item.children.map((item) => !item.hasOwnDocument && context.member(item)))} + {props.groups?.map((item) => item.children.map((i) => !i.hasOwnDocument && context.member(i)))} ); -} +}; diff --git a/src/partials/members/members.tsx b/src/Theme/Partials/Members/members.tsx similarity index 79% rename from src/partials/members/members.tsx rename to src/Theme/Partials/Members/members.tsx index 6058bad..398b738 100755 --- a/src/partials/members/members.tsx +++ b/src/Theme/Partials/Members/members.tsx @@ -10,10 +10,21 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Members + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; -import type { DefaultThemeRenderContext, ContainerReflection } from 'typedoc'; +import type { + ContainerReflection, + DeclarationReflection, + DefaultThemeRenderContext, + DocumentReflection +} from 'typedoc'; -export function filterMap(iter: Iterable | undefined, fn: (item: T) => U | undefined): U[] { +export const filterMap = (iter: Iterable | undefined, fn: (item: T) => U | undefined): U[] => { const result: U[] = []; for (const item of iter || []) { @@ -24,9 +35,11 @@ export function filterMap(iter: Iterable | undefined, fn: (item: T) => } return result; -} +}; -function getMemberSections(parent: ContainerReflection) { +const getMemberSections = ( + parent: ContainerReflection +): { title: string; children: (DocumentReflection | DeclarationReflection)[] }[] => { if (parent.categories?.length) { return filterMap(parent.categories, (cat) => { if (!cat.allChildrenHaveOwnDocument()) { @@ -61,9 +74,9 @@ function getMemberSections(parent: ContainerReflection) { } return []; -} +}; -export function members(context: DefaultThemeRenderContext, props: ContainerReflection) { +export const members = (context: DefaultThemeRenderContext, props: ContainerReflection): JSX.Element => { const sections = getMemberSections(props).filter((sect) => sect.children.length); return ( @@ -78,10 +91,10 @@ export function members(context: DefaultThemeRenderContext, props: ContainerRefl {context.icons.chevronDown()} {title} -
    {children.map((item) => context.member(item))}
    +
    {children.map((i) => context.member(i))}
    ); })} ); -} +}; diff --git a/src/Theme/Partials/Navigation/PageSidebar/index.ts b/src/Theme/Partials/Navigation/PageSidebar/index.ts new file mode 100644 index 0000000..42defe3 --- /dev/null +++ b/src/Theme/Partials/Navigation/PageSidebar/index.ts @@ -0,0 +1,24 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module contains the components that are used when rendering the page sidebar. + * + * @module Theme/Partials/Navigation/PageSidebar + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './pageNavigation'; +export * from './pageSidebar'; +export * from './settings'; diff --git a/src/partials/navigation/pageSidebar/pageNavigation.tsx b/src/Theme/Partials/Navigation/PageSidebar/pageNavigation.tsx similarity index 82% rename from src/partials/navigation/pageSidebar/pageNavigation.tsx rename to src/Theme/Partials/Navigation/PageSidebar/pageNavigation.tsx index 424f41d..6d2ca3e 100644 --- a/src/partials/navigation/pageSidebar/pageNavigation.tsx +++ b/src/Theme/Partials/Navigation/PageSidebar/pageNavigation.tsx @@ -10,23 +10,29 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { Reflection, JSX, PageEvent, PageHeading, DefaultThemeRenderContext } from 'typedoc'; -import { wbr } from '../../lib'; +/** + * @module Theme/Partials/Navigation/PageSidebar + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, PageEvent, PageHeading, Reflection } from 'typedoc'; -function buildSectionNavigation(context: DefaultThemeRenderContext, headings: PageHeading[]) { +import { wbr } from '../../../../Utils/lib'; + +const buildSectionNavigation = (context: DefaultThemeRenderContext, headings: PageHeading[]): JSX.Element[] => { const levels: JSX.Element[][] = [[]]; - function finalizeLevel(finishedHandlingHeadings: boolean) { + const finalizeLevel = (finishedHandlingHeadings: boolean): void => { const level = levels.pop(); if (level !== undefined && levels[levels.length - 1].length === 0 && finishedHandlingHeadings) { levels[levels.length - 1] = level; return; } - const built =
      {level && level.map((l) =>
    • {l}
    • )}
    ; + const built =
      {level?.map((l) =>
    • {l}
    • )}
    ; levels[levels.length - 1].push(built); - } + }; for (const heading of headings) { const inferredLevel = heading.level @@ -57,9 +63,9 @@ function buildSectionNavigation(context: DefaultThemeRenderContext, headings: Pa levels.unshift([]); finalizeLevel(true); return levels[0]; -} +}; -export function pageNavigation(context: DefaultThemeRenderContext, props: PageEvent) { +export const pageNavigation = (context: DefaultThemeRenderContext, props: PageEvent): JSX.Element => { if (!props.pageSections.some((sect) => sect.headings.length)) { return <>; } @@ -93,4 +99,4 @@ export function pageNavigation(context: DefaultThemeRenderContext, props: PageEv
    {sections}
    ); -} +}; diff --git a/src/templates/index.tsx b/src/Theme/Partials/Navigation/PageSidebar/pageSidebar.tsx old mode 100755 new mode 100644 similarity index 60% rename from src/templates/index.tsx rename to src/Theme/Partials/Navigation/PageSidebar/pageSidebar.tsx index f541e22..2034de1 --- a/src/templates/index.tsx +++ b/src/Theme/Partials/Navigation/PageSidebar/pageSidebar.tsx @@ -10,11 +10,17 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import type { DefaultThemeRenderContext, ProjectReflection, PageEvent } from 'typedoc'; -import { JSX } from 'typedoc'; -export const indexTemplate = ({ markdown }: DefaultThemeRenderContext, props: PageEvent) => ( -
    - -
    +/** + * @module Theme/Partials/Navigation/PageSidebar + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, PageEvent, Reflection } from 'typedoc'; + +export const pageSidebar = (context: DefaultThemeRenderContext, props: PageEvent): JSX.Element => ( + <> + {context.settings()} + {context.pageNavigation(props)} + ); diff --git a/src/partials/navigation/pageSidebar/settings.tsx b/src/Theme/Partials/Navigation/PageSidebar/settings.tsx similarity index 83% rename from src/partials/navigation/pageSidebar/settings.tsx rename to src/Theme/Partials/Navigation/PageSidebar/settings.tsx index bccd571..f983ac9 100644 --- a/src/partials/navigation/pageSidebar/settings.tsx +++ b/src/Theme/Partials/Navigation/PageSidebar/settings.tsx @@ -10,7 +10,13 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { ReflectionFlag, JSX, DefaultThemeRenderContext } from 'typedoc'; + +/** + * @module Theme/Partials/Navigation/PageSidebar + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, ReflectionFlag } from 'typedoc'; const flagOptionNameToReflectionFlag = { protected: ReflectionFlag.Protected, @@ -19,19 +25,22 @@ const flagOptionNameToReflectionFlag = { inherited: ReflectionFlag.Inherited }; -function buildFilterItem(context: DefaultThemeRenderContext, name: string, displayName: string, defaultValue: boolean) { - return ( -
  • - -
  • - ); -} +const buildFilterItem = ( + context: DefaultThemeRenderContext, + name: string, + displayName: string, + defaultValue: boolean +): JSX.Element => ( +
  • + +
  • +); -export function settings(context: DefaultThemeRenderContext) { +export const settings = (context: DefaultThemeRenderContext): JSX.Element => { const defaultFilters = context.options.getValue('visibilityFilters') as Record; const visibilityOptions: JSX.Element[] = []; @@ -100,4 +109,4 @@ export function settings(context: DefaultThemeRenderContext) {
    ); -} +}; diff --git a/src/Theme/Partials/Navigation/Sidebar/index.ts b/src/Theme/Partials/Navigation/Sidebar/index.ts new file mode 100644 index 0000000..32879ec --- /dev/null +++ b/src/Theme/Partials/Navigation/Sidebar/index.ts @@ -0,0 +1,24 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module contains the components that are used when rendering the sidebar. + * + * @module Theme/Partials/Navigation/Sidebar + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './navigation'; +export * from './sidebar'; +export * from './sidebarLinks'; diff --git a/src/Theme/Partials/Navigation/Sidebar/navigation.tsx b/src/Theme/Partials/Navigation/Sidebar/navigation.tsx new file mode 100644 index 0000000..97fbe5f --- /dev/null +++ b/src/Theme/Partials/Navigation/Sidebar/navigation.tsx @@ -0,0 +1,33 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * @module Theme/Partials/Navigation/Sidebar + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, PageEvent, Reflection, ReflectionKind } from 'typedoc'; + +import { classNames } from '../../../../Utils/lib'; + +export const navigation = (context: DefaultThemeRenderContext, props: PageEvent): JSX.Element => ( + +); diff --git a/src/templates/document.tsx b/src/Theme/Partials/Navigation/Sidebar/sidebar.tsx old mode 100755 new mode 100644 similarity index 61% rename from src/templates/document.tsx rename to src/Theme/Partials/Navigation/Sidebar/sidebar.tsx index fff1e1d..1ef3732 --- a/src/templates/document.tsx +++ b/src/Theme/Partials/Navigation/Sidebar/sidebar.tsx @@ -10,11 +10,17 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import type { DefaultThemeRenderContext, DocumentReflection } from 'typedoc'; -import { JSX, PageEvent } from 'typedoc'; -export const documentTemplate = ({ markdown }: DefaultThemeRenderContext, props: PageEvent) => ( -
    - -
    +/** + * @module Theme/Partials/Navigation/Sidebar + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, PageEvent, Reflection } from 'typedoc'; + +export const sidebar = (context: DefaultThemeRenderContext, props: PageEvent): JSX.Element => ( + <> + {context.sidebarLinks()} + {context.navigation(props)} + ); diff --git a/src/partials/navigation/sidebar/sidebarLinks.tsx b/src/Theme/Partials/Navigation/Sidebar/sidebarLinks.tsx similarity index 82% rename from src/partials/navigation/sidebar/sidebarLinks.tsx rename to src/Theme/Partials/Navigation/Sidebar/sidebarLinks.tsx index df840cb..5aa155f 100644 --- a/src/partials/navigation/sidebar/sidebarLinks.tsx +++ b/src/Theme/Partials/Navigation/Sidebar/sidebarLinks.tsx @@ -10,9 +10,15 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { JSX, DefaultThemeRenderContext } from 'typedoc'; -export function sidebarLinks(context: DefaultThemeRenderContext) { +/** + * @module Theme/Partials/Navigation/Sidebar + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX } from 'typedoc'; + +export const sidebarLinks = (context: DefaultThemeRenderContext): JSX.Element | null => { const links = Object.entries(context.options.getValue('sidebarLinks')); const navLinks = Object.entries(context.options.getValue('navigationLinks')); @@ -30,4 +36,4 @@ export function sidebarLinks(context: DefaultThemeRenderContext) { ))} ); -} +}; diff --git a/src/Theme/Partials/Navigation/Toolbar/index.ts b/src/Theme/Partials/Navigation/Toolbar/index.ts new file mode 100644 index 0000000..e0e3549 --- /dev/null +++ b/src/Theme/Partials/Navigation/Toolbar/index.ts @@ -0,0 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module contains the components that are used when rendering the toolbar. + * + * @module Theme/Partials/Navigation/Toolbar + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './toolbar'; diff --git a/src/partials/navigation/toolbar/toolbar.tsx b/src/Theme/Partials/Navigation/Toolbar/toolbar.tsx similarity index 84% rename from src/partials/navigation/toolbar/toolbar.tsx rename to src/Theme/Partials/Navigation/Toolbar/toolbar.tsx index a196803..8b0f5f1 100755 --- a/src/partials/navigation/toolbar/toolbar.tsx +++ b/src/Theme/Partials/Navigation/Toolbar/toolbar.tsx @@ -10,12 +10,18 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Navigation/Toolbar + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; -import type { Reflection, PageEvent, DefaultThemeRenderContext } from 'typedoc'; +import type { DefaultThemeRenderContext, PageEvent, Reflection } from 'typedoc'; -import { getDisplayName } from '../../lib'; +import { getDisplayName } from '../../../../Utils/lib'; -export const toolbar = (context: DefaultThemeRenderContext, props: PageEvent) => ( +export const toolbar = (context: DefaultThemeRenderContext, props: PageEvent): JSX.Element => (
    @@ -57,9 +63,13 @@ export const toolbar = (context: DefaultThemeRenderContext, props: PageEvent - Go to repository + Go to repository
    diff --git a/src/Theme/Partials/Navigation/index.ts b/src/Theme/Partials/Navigation/index.ts new file mode 100644 index 0000000..1c54dde --- /dev/null +++ b/src/Theme/Partials/Navigation/index.ts @@ -0,0 +1,25 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module contains the components that are used when rendering the different + * navigation elements of the page. + * + * @module Theme/Partials/Navigation + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './PageSidebar'; +export * from './Sidebar'; +export * from './Toolbar'; diff --git a/src/partials/sections/breadcrumb.tsx b/src/Theme/Partials/Sections/breadcrumb.tsx similarity index 92% rename from src/partials/sections/breadcrumb.tsx rename to src/Theme/Partials/Sections/breadcrumb.tsx index 9d58561..841b688 100755 --- a/src/partials/sections/breadcrumb.tsx +++ b/src/Theme/Partials/Sections/breadcrumb.tsx @@ -10,6 +10,12 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Sections + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; import type { DefaultThemeRenderContext, Reflection } from 'typedoc'; diff --git a/src/partials/sections/footer.tsx b/src/Theme/Partials/Sections/footer.tsx similarity index 91% rename from src/partials/sections/footer.tsx rename to src/Theme/Partials/Sections/footer.tsx index 77b4219..2844610 100755 --- a/src/partials/sections/footer.tsx +++ b/src/Theme/Partials/Sections/footer.tsx @@ -10,11 +10,17 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Sections + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; import type { GobstonesThemeContext } from '../../GobstonesThemeContext'; -export function footer(context: GobstonesThemeContext) { +export const footer = (context: GobstonesThemeContext): JSX.Element => { const hideGenerator = context.options.getValue('hideGenerator'); let generatorDisplay = <>; if (!hideGenerator) { @@ -22,7 +28,7 @@ export function footer(context: GobstonesThemeContext) { // Only handles one occurrence, but that's all I expect... const index = message.indexOf('TypeDoc'); - if (index == -1) { + if (index === -1) { generatorDisplay =

    {message}

    ; } else { const pre = message.substring(0, index); @@ -61,4 +67,4 @@ export function footer(context: GobstonesThemeContext) { {context.hook('footer.end', context)} ); -} +}; diff --git a/src/partials/sections/header.tsx b/src/Theme/Partials/Sections/header.tsx similarity index 89% rename from src/partials/sections/header.tsx rename to src/Theme/Partials/Sections/header.tsx index df4a527..acb33dc 100755 --- a/src/partials/sections/header.tsx +++ b/src/Theme/Partials/Sections/header.tsx @@ -10,11 +10,17 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { JSX, DefaultThemeRenderContext, PageEvent, Reflection, ReflectionKind } from 'typedoc'; -import { classNames, getDisplayName, hasTypeParameters, join } from '../lib'; +/** + * @module Theme/Partials/Sections + * @author Alan Rodas Bonjour + */ + +import { DefaultThemeRenderContext, JSX, PageEvent, Reflection, ReflectionKind } from 'typedoc'; + +import { classNames, getDisplayName, hasTypeParameters, join } from '../../../Utils/lib'; -export const header = (context: DefaultThemeRenderContext, props: PageEvent) => { +export const header = (context: DefaultThemeRenderContext, props: PageEvent): JSX.Element => { const HeadingLevel = props.model.isProject() ? 'h2' : 'h1'; return (
    diff --git a/src/Theme/Partials/Sections/index.ts b/src/Theme/Partials/Sections/index.ts new file mode 100644 index 0000000..74a66e8 --- /dev/null +++ b/src/Theme/Partials/Sections/index.ts @@ -0,0 +1,25 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module contains the components that are used when rendering the + * different main sections of the page, such as the footer, header and breadcrumbs. + * + * @module Theme/Partials/Sections + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './breadcrumb'; +export * from './footer'; +export * from './header'; diff --git a/src/Theme/Partials/Types/index.ts b/src/Theme/Partials/Types/index.ts new file mode 100644 index 0000000..a252c6f --- /dev/null +++ b/src/Theme/Partials/Types/index.ts @@ -0,0 +1,24 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module contains the components that are used when rendering types. + * + * @module Theme/Partials/Types + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './type'; +export * from './typeAndParent'; +export * from './typeParameters'; diff --git a/src/partials/types/type.tsx b/src/Theme/Partials/Types/type.tsx similarity index 70% rename from src/partials/types/type.tsx rename to src/Theme/Partials/Types/type.tsx index b8dec50..d10ba17 100755 --- a/src/partials/types/type.tsx +++ b/src/Theme/Partials/Types/type.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-bitwise */ /* * ***************************************************************************** * Copyright (C) National University of Quilmes 2018-2024 @@ -10,11 +11,18 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials/Types + * @author Alan Rodas Bonjour + */ + import { ok } from 'assert'; import { - DefaultThemeRenderContext, DeclarationReflection, + DefaultThemeRenderContext, + JSX, LiteralType, ProjectReflection, ReferenceType, @@ -22,12 +30,14 @@ import { ReflectionKind, Type, TypeContext, - TypeKindMap, - JSX + TypeKindMap } from 'typedoc'; -import { getKindClass, join, stringify } from '../lib'; +import { getKindClass, join, stringify } from '../../../Utils/lib'; +/** + * The exportable reflection kinds + */ const EXPORTABLE: ReflectionKind = ReflectionKind.Class | ReflectionKind.Interface | @@ -36,8 +46,20 @@ const EXPORTABLE: ReflectionKind = ReflectionKind.Function | ReflectionKind.Variable; +/** + * A map to avoid name collisions. + */ const nameCollisionCache = new WeakMap>(); -function getNameCollisionCount(project: ProjectReflection, name: string): number { + +/** + * Returns the number of collisions of the given name in the given project reflection. + * + * @param project - The project reflection to count the number of collisions in. + * @param name - The name to test the number of collisions. + * + * @returns The number of collisions for the name in the project. + */ +const getNameCollisionCount = (project: ProjectReflection, name: string): number => { let collisions = nameCollisionCache.get(project); if (collisions === undefined) { collisions = {}; @@ -47,7 +69,7 @@ function getNameCollisionCount(project: ProjectReflection, name: string): number nameCollisionCache.set(project, collisions); } return collisions[name] ?? 0; -} +}; /** * Returns a (hopefully) globally unique path for the given reflection. @@ -57,16 +79,28 @@ function getNameCollisionCount(project: ProjectReflection, name: string): number * If the given reflection has a globally unique name already, then it will be returned as is. If the name is * ambiguous (i.e. there are two classes with the same name in different namespaces), then the namespaces path of the * reflection will be returned. + * + * @param reflection - The reflection to get the path for. + * + * @returns A unique path for the reflection. */ -function getUniquePath(reflection: Reflection): Reflection[] { +const getUniquePath = (reflection: Reflection): Reflection[] => { if (reflection.kindOf(EXPORTABLE)) { if (getNameCollisionCount(reflection.project, reflection.name) >= 2) { return getNamespacedPath(reflection); } } return [reflection]; -} -function getNamespacedPath(reflection: Reflection): Reflection[] { +}; + +/** + * Return the namespace path for the given reflection. + * + * @param reflection - The reflection to get the namespace path. + * + * @returns The namespace path. + */ +const getNamespacedPath = (reflection: Reflection): Reflection[] => { const path = [reflection]; let parent = reflection.parent; while (parent?.kindOf(ReflectionKind.Namespace)) { @@ -74,27 +108,44 @@ function getNamespacedPath(reflection: Reflection): Reflection[] { parent = parent.parent; } return path; -} -function renderUniquePath(context: DefaultThemeRenderContext, reflection: Reflection): JSX.Element { - return join(., getUniquePath(reflection), (item) => ( +}; + +/** + * Render the unique path of the given reflection in the context of the theme, + * returning the corresponding JSX.Element that links to the path item. + * + * @param context - The theme render context + * @param reflection - The reflection to render the path of. + * @returns A JSX element that renders the reflection path. + */ +const renderUniquePath = (context: DefaultThemeRenderContext, reflection: Reflection): JSX.Element => + join(., getUniquePath(reflection), (item) => ( {item.name} )); -} +/** + * The indentation size to use + */ const indentSize = 4; +/** + * The current depth of the indentation + */ let indentationDepth = 0; -function includeIndentation(): JSX.Element { - return indentationDepth > 0 ? {'\u00A0'.repeat(indentationDepth * indentSize)} : <>; -} +/** + * Returns a JSX Element that represents the indentation of an element using the + * indentation depth. + */ +const includeIndentation = (): JSX.Element => + indentationDepth > 0 ? {'\u00A0'.repeat(indentationDepth * indentSize)} : <>; -export function validateStateIsClean(page: string) { +export const validateStateIsClean = (page: string): void => { ok( indentationDepth === 0, `Rendering ${page}: Indentation depth increment/decrement not matched: ${indentationDepth.toString()}` ); -} +}; // The type helper accepts an optional needsParens parameter that is checked // if an inner type may result in invalid output without them. For example: @@ -107,15 +158,13 @@ const typeRenderers: { options: { topLevelLinks: boolean } ) => JSX.Element; } = { - array(context, type) { - return ( - <> - {renderType(context, type.elementType, TypeContext.arrayElement)} - [] - - ); - }, - conditional(context, type) { + array: (context, type) => ( + <> + {renderType(context, type.elementType, TypeContext.arrayElement)} + [] + + ), + conditional: (context, type) => { indentationDepth++; const parts: JSX.Element[] = [ renderType(context, type.checkType, TypeContext.conditionalCheck), @@ -134,7 +183,7 @@ const typeRenderers: { return <>{parts}; }, - indexedAccess(context, type) { + indexedAccess: (context, type) => { let indexType: JSX.Element = renderType(context, type.indexType, TypeContext.indexedIndex); if ( @@ -158,32 +207,24 @@ const typeRenderers: { ); }, - inferred(context, type) { - return ( - <> - infer {' '} - {type.name} - {type.constraint && ( - <> - extends - {renderType(context, type.constraint, TypeContext.inferredConstraint)} - - )} - - ); - }, - intersection(context, type) { - return join( & , type.types, (item) => + inferred: (context, type) => ( + <> + infer {type.name} + {type.constraint && ( + <> + extends + {renderType(context, type.constraint, TypeContext.inferredConstraint)} + + )} + + ), + intersection: (context, type) => + join( & , type.types, (item) => renderType(context, item, TypeContext.intersectionElement) - ); - }, - intrinsic(_context, type) { - return {type.name}; - }, - literal(_context, type) { - return {stringify(type.value)}; - }, - mapped(context, type) { + ), + intrinsic: (_context, type) => {type.name}, + literal: (_context, type) => {stringify(type.value)}, + mapped: (context, type) => { indentationDepth++; const parts = [{'{'},
    , includeIndentation()]; @@ -199,6 +240,8 @@ const typeRenderers: { ); break; + default: + break; } parts.push( @@ -241,50 +284,42 @@ const typeRenderers: { ); }, - namedTupleMember(context, type) { - return ( - <> - {type.name} - {type.isOptional ? ( - ?: - ) : ( - : - )} - {renderType(context, type.element, TypeContext.tupleElement)} - - ); - }, - optional(context, type) { - return ( - <> - {renderType(context, type.elementType, TypeContext.optionalElement)} - ? - - ); - }, - predicate(context, type) { - return ( - <> - {!!type.asserts && asserts } - {type.name} - {!!type.targetType && ( - <> - is - {renderType(context, type.targetType, TypeContext.predicateTarget)} - - )} - - ); - }, - query(context, type) { - return ( - <> - typeof - {renderType(context, type.queryType, TypeContext.queryTypeTarget)} - - ); - }, - reference(context, type) { + namedTupleMember: (context, type) => ( + <> + {type.name} + {type.isOptional ? ( + ?: + ) : ( + : + )} + {renderType(context, type.element, TypeContext.tupleElement)} + + ), + optional: (context, type) => ( + <> + {renderType(context, type.elementType, TypeContext.optionalElement)} + ? + + ), + predicate: (context, type) => ( + <> + {!!type.asserts && asserts } + {type.name} + {!!type.targetType && ( + <> + is + {renderType(context, type.targetType, TypeContext.predicateTarget)} + + )} + + ), + query: (context, type) => ( + <> + typeof + {renderType(context, type.queryType, TypeContext.queryTypeTarget)} + + ), + reference: (context, type) => { const reflection = type.reflection; let name: JSX.Element; @@ -326,13 +361,13 @@ const typeRenderers: { return name; }, - reflection(context, type, { topLevelLinks }) { + reflection: (context, type, { topLevelLinks }) => { const members: JSX.Element[] = []; const children: DeclarationReflection[] = type.declaration.children || []; indentationDepth++; - const renderName = (named: Reflection) => + const renderName = (named: Reflection): JSX.Element => topLevelLinks ? ( {named.name} @@ -371,11 +406,11 @@ const typeRenderers: { set {renderName(item.setSignature)} ( - {item.setSignature.parameters?.map((item) => ( + {item.setSignature.parameters?.map((i) => ( <> - {item.name} + {i.name} : - {renderType(context, item.type, TypeContext.none)} + {renderType(context, i.type, TypeContext.none)} ))} ) @@ -468,51 +503,43 @@ const typeRenderers: { indentationDepth--; return {'{}'}; }, - rest(context, type) { - return ( - <> - ... - {renderType(context, type.elementType, TypeContext.restElement)} - - ); - }, - templateLiteral(context, type) { - return ( - <> - ` - {type.head && {type.head}} - {type.tail.map((item) => ( - <> - {'${'} - {renderType(context, item[0], TypeContext.templateLiteralElement)} - {'}'} - {item[1] && {item[1]}} - - ))} - ` - - ); - }, - tuple(context, type) { - return ( - <> - [ - {join(, , type.elements, (item) => - renderType(context, item, TypeContext.tupleElement) - )} - ] - - ); - }, - typeOperator(context, type) { - return ( - <> - {type.operator} - {renderType(context, type.target, TypeContext.typeOperatorTarget)} - - ); - }, - union(context, type) { + rest: (context, type) => ( + <> + ... + {renderType(context, type.elementType, TypeContext.restElement)} + + ), + templateLiteral: (context, type) => ( + <> + ` + {type.head && {type.head}} + {type.tail.map((item) => ( + <> + {'${'} + {renderType(context, item[0], TypeContext.templateLiteralElement)} + {'}'} + {item[1] && {item[1]}} + + ))} + ` + + ), + tuple: (context, type) => ( + <> + [ + {join(, , type.elements, (item) => + renderType(context, item, TypeContext.tupleElement) + )} + ] + + ), + typeOperator: (context, type) => ( + <> + {type.operator} + {renderType(context, type.target, TypeContext.typeOperatorTarget)} + + ), + union: (context, type) => { // This could likely be improved with some print width based heuristic like // how prettier works, but my initial investigation with it didn't consistently // produce better results than this much simpler method as the print width @@ -542,17 +569,15 @@ const typeRenderers: { renderType(context, item, TypeContext.unionElement) ); }, - unknown(_context, type) { - return <>{type.name}; - } + unknown: (_context, type) => <>{type.name} }; -function renderType( +const renderType = ( context: DefaultThemeRenderContext, type: Type | undefined, where: TypeContext, options: { topLevelLinks: boolean } = { topLevelLinks: false } -) { +): JSX.Element => { if (!type) { return any; } @@ -571,12 +596,10 @@ function renderType( } return rendered; -} +}; -export function type( +export const type = ( context: DefaultThemeRenderContext, - type: Type | undefined, + requiredType: Type | undefined, options: { topLevelLinks: boolean } = { topLevelLinks: false } -) { - return renderType(context, type, TypeContext.none, options); -} +): JSX.Element => renderType(context, requiredType, TypeContext.none, options); diff --git a/src/partials/types/typeAndParent.tsx b/src/Theme/Partials/Types/typeAndParent.tsx similarity index 90% rename from src/partials/types/typeAndParent.tsx rename to src/Theme/Partials/Types/typeAndParent.tsx index 31a7a32..287530b 100755 --- a/src/partials/types/typeAndParent.tsx +++ b/src/Theme/Partials/Types/typeAndParent.tsx @@ -10,7 +10,13 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { JSX, DefaultThemeRenderContext, ArrayType, ReferenceType, SignatureReflection, Type } from 'typedoc'; + +/** + * @module Theme/Partials/Types + * @author Alan Rodas Bonjour + */ + +import { ArrayType, DefaultThemeRenderContext, JSX, ReferenceType, SignatureReflection, Type } from 'typedoc'; export const typeAndParent = (context: DefaultThemeRenderContext, props: Type): JSX.Element => { if (props instanceof ArrayType) { diff --git a/src/Theme/Partials/Types/typeParameters.tsx b/src/Theme/Partials/Types/typeParameters.tsx new file mode 100755 index 0000000..ac46e36 --- /dev/null +++ b/src/Theme/Partials/Types/typeParameters.tsx @@ -0,0 +1,56 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * @module Theme/Partials/Types + * @author Alan Rodas Bonjour + */ + +import type { DefaultThemeRenderContext, TypeParameterReflection } from 'typedoc'; +import { JSX } from 'typedoc'; + +export const typeParameters = (context: DefaultThemeRenderContext, params: TypeParameterReflection[]): JSX.Element => ( + <> +
    +

    {context.i18n.kind_plural_type_parameter()}

    +
      + {params.map((item) => ( +
    • + + + {item.flags.isConst && const } + {item.varianceModifier && ( + {item.varianceModifier} + )} + {item.name} + {!!item.type && ( + <> + extends + {context.type(item.type)} + + )} + {!!item.default && ( + <> + {' = '} + {context.type(item.default)} + + )} + + {context.commentSummary(item)} + {context.commentTags(item)} +
    • + ))} +
    +
    + +); diff --git a/src/partials/anchor-icon.tsx b/src/Theme/Partials/anchor-icon.tsx similarity index 81% rename from src/partials/anchor-icon.tsx rename to src/Theme/Partials/anchor-icon.tsx index 9a13b85..9d4b986 100755 --- a/src/partials/anchor-icon.tsx +++ b/src/Theme/Partials/anchor-icon.tsx @@ -10,10 +10,16 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; import type { DefaultThemeRenderContext } from 'typedoc'; -export function anchorIcon(context: DefaultThemeRenderContext, anchor: string | undefined) { +export const anchorIcon = (context: DefaultThemeRenderContext, anchor: string | undefined): JSX.Element => { if (!anchor) return <>; return ( @@ -21,4 +27,4 @@ export function anchorIcon(context: DefaultThemeRenderContext, anchor: string | {context.icons.anchor()} ); -} +}; diff --git a/src/partials/hierarchy.tsx b/src/Theme/Partials/hierarchy.tsx similarity index 63% rename from src/partials/hierarchy.tsx rename to src/Theme/Partials/hierarchy.tsx index 8ec02d8..474be79 100755 --- a/src/partials/hierarchy.tsx +++ b/src/Theme/Partials/hierarchy.tsx @@ -10,23 +10,32 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; -import type { DefaultThemeRenderContext, DeclarationHierarchy, Type } from 'typedoc'; +import type { DeclarationHierarchy, DefaultThemeRenderContext, Type } from 'typedoc'; -const isLinkedReferenceType = (type: Type) => +const isLinkedReferenceType = (type: Type): boolean => type.visit({ reference: (ref) => ref.reflection !== undefined }) ?? false; -function hasAnyLinkedReferenceType(h: DeclarationHierarchy | undefined): boolean { +const hasAnyLinkedReferenceType = (h: DeclarationHierarchy | undefined): boolean => { if (!h) return false; if (!h.isTarget && h.types.some(isLinkedReferenceType)) return true; return hasAnyLinkedReferenceType(h.next); -} +}; -export function hierarchy(context: DefaultThemeRenderContext, props: DeclarationHierarchy | undefined) { +export const hierarchy = ( + context: DefaultThemeRenderContext, + props: DeclarationHierarchy | undefined +): JSX.Element | undefined => { if (!props) return; const fullLink = hasAnyLinkedReferenceType(props) ? ( @@ -51,17 +60,15 @@ export function hierarchy(context: DefaultThemeRenderContext, props: Declaration {hierarchyList(context, props)} ); -} +}; -function hierarchyList(context: DefaultThemeRenderContext, props: DeclarationHierarchy) { - return ( -
      - {props.types.map((item, i, l) => ( -
    • - {props.isTarget ? {item.toString()} : context.type(item)} - {i === l.length - 1 && !!props.next && hierarchyList(context, props.next)} -
    • - ))} -
    - ); -} +const hierarchyList = (context: DefaultThemeRenderContext, props: DeclarationHierarchy): JSX.Element => ( +
      + {props.types.map((item, i, l) => ( +
    • + {props.isTarget ? {item.toString()} : context.type(item)} + {i === l.length - 1 && !!props.next && hierarchyList(context, props.next)} +
    • + ))} +
    +); diff --git a/src/Theme/Partials/index.ts b/src/Theme/Partials/index.ts new file mode 100644 index 0000000..4b52eb6 --- /dev/null +++ b/src/Theme/Partials/index.ts @@ -0,0 +1,35 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module exports all the components used when rendering the elements + * of the page. + * + * + * @module Theme/Partials + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './Comments'; +export * from './Members'; +export * from './Navigation'; +export * from './Sections'; +export * from './Types'; + +export * from './anchor-icon'; +export * from './hierarchy'; +export * from '../Icons/icon'; +export * from './indexElement'; +export * from './parameter'; +export * from './reflectionPreview'; diff --git a/src/partials/index.tsx b/src/Theme/Partials/indexElement.tsx similarity index 65% rename from src/partials/index.tsx rename to src/Theme/Partials/indexElement.tsx index dd07a69..9859dab 100755 --- a/src/partials/index.tsx +++ b/src/Theme/Partials/indexElement.tsx @@ -10,46 +10,50 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials + * @author Alan Rodas Bonjour + */ + import { JSX } from 'typedoc'; -import type { DefaultThemeRenderContext, ContainerReflection, ReflectionCategory, ReflectionGroup } from 'typedoc'; +import type { ContainerReflection, DefaultThemeRenderContext, ReflectionCategory, ReflectionGroup } from 'typedoc'; -import { classNames, renderName } from './lib'; +import { classNames, renderName } from '../../Utils/lib'; -function renderCategory( +const renderCategory = ( { urlTo, icons, getReflectionClasses, markdown }: DefaultThemeRenderContext, item: ReflectionCategory | ReflectionGroup, prependName = '' -) { - return ( -
    -

    {prependName ? `${prependName} - ${item.title}` : item.title}

    - {item.description && ( -
    - -
    - )} -
    - {item.children.map((item) => ( - <> - - {icons[item.kind]()} - {renderName(item)} - - {'\n'} - - ))} +): JSX.Element => ( +
    +

    {prependName ? `${prependName} - ${item.title}` : item.title}

    + {item.description && ( +
    +
    -
    - ); -} + )} +
    + {item.children.map((i) => ( + <> + + {icons[i.kind]()} + {renderName(i)} + + {'\n'} + + ))} +
    +
    +); -export function index(context: DefaultThemeRenderContext, props: ContainerReflection) { +export const index = (context: DefaultThemeRenderContext, props: ContainerReflection): JSX.Element => { let content: JSX.Element | JSX.Element[] = []; if (props.categories?.length) { @@ -94,4 +98,4 @@ export function index(context: DefaultThemeRenderContext, props: ContainerReflec ); -} +}; diff --git a/src/partials/parameter.tsx b/src/Theme/Partials/parameter.tsx similarity index 84% rename from src/partials/parameter.tsx rename to src/Theme/Partials/parameter.tsx index a1c6e74..8fd8dde 100755 --- a/src/partials/parameter.tsx +++ b/src/Theme/Partials/parameter.tsx @@ -10,11 +10,17 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ -import { DefaultThemeRenderContext, DeclarationReflection, ReflectionType, SignatureReflection, JSX } from 'typedoc'; -import { classNames, getKindClass, wbr } from './lib'; +/** + * @module Theme/Partials + * @author Alan Rodas Bonjour + */ + +import { DeclarationReflection, DefaultThemeRenderContext, JSX, ReflectionType, SignatureReflection } from 'typedoc'; -export const parameter = (context: DefaultThemeRenderContext, props: DeclarationReflection) => ( +import { classNames, getKindClass, wbr } from '../../Utils/lib'; + +export const parameter = (context: DefaultThemeRenderContext, props: DeclarationReflection): JSX.Element => ( <>
      {!!props.signatures && ( @@ -100,11 +106,11 @@ export const parameter = (context: DefaultThemeRenderContext, props: Declaration set {wbr(item.name)} ( - {item.setSignature.parameters?.map((item) => ( + {item.setSignature.parameters?.map((i) => ( <> - {item.name} + {i.name} : - {context.type(item.type)} + {context.type(i.type)} ))} ): @@ -124,24 +130,22 @@ export const parameter = (context: DefaultThemeRenderContext, props: Declaration ); -function renderParamIndexSignature(context: DefaultThemeRenderContext, index: SignatureReflection) { - return ( -
    • -
      - [ - {index.parameters?.map((item) => ( - <> - {item.name} - {': '} - {context.type(item.type)} - - ))} - {']: '} - {context.type(index.type)} -
      - {context.commentSummary(index)} - {context.commentTags(index)} - {index.type instanceof ReflectionType && context.parameter(index.type.declaration)} -
    • - ); -} +const renderParamIndexSignature = (context: DefaultThemeRenderContext, index: SignatureReflection): JSX.Element => ( +
    • +
      + [ + {index.parameters?.map((item) => ( + <> + {item.name} + {': '} + {context.type(item.type)} + + ))} + {']: '} + {context.type(index.type)} +
      + {context.commentSummary(index)} + {context.commentTags(index)} + {index.type instanceof ReflectionType && context.parameter(index.type.declaration)} +
    • +); diff --git a/src/partials/reflectionPreview.tsx b/src/Theme/Partials/reflectionPreview.tsx similarity index 83% rename from src/partials/reflectionPreview.tsx rename to src/Theme/Partials/reflectionPreview.tsx index 4555217..6373a08 100755 --- a/src/partials/reflectionPreview.tsx +++ b/src/Theme/Partials/reflectionPreview.tsx @@ -10,18 +10,24 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Partials + * @author Alan Rodas Bonjour + */ + import { DeclarationReflection, - ReflectionKind, - Reflection, - ReflectionType, DefaultThemeRenderContext, - JSX + JSX, + Reflection, + ReflectionKind, + ReflectionType } from 'typedoc'; -import { getKindClass, renderTypeParametersSignature } from './lib'; +import { getKindClass, renderTypeParametersSignature } from '../../Utils/lib'; -export function reflectionPreview(context: DefaultThemeRenderContext, props: Reflection) { +export const reflectionPreview = (context: DefaultThemeRenderContext, props: Reflection): JSX.Element | undefined => { if (!(props instanceof DeclarationReflection)) return; // Each property of the interface will have a member rendered later on the page describing it, so generate @@ -38,4 +44,4 @@ export function reflectionPreview(context: DefaultThemeRenderContext, props: Ref ); } return undefined; -} +}; diff --git a/src/Theme/Templates/documentTemplate.tsx b/src/Theme/Templates/documentTemplate.tsx new file mode 100755 index 0000000..c04d9b3 --- /dev/null +++ b/src/Theme/Templates/documentTemplate.tsx @@ -0,0 +1,38 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * @module Theme/Templates + * @author Alan Rodas Bonjour + */ + +import type { DefaultThemeRenderContext, DocumentReflection } from 'typedoc'; +import { JSX, PageEvent } from 'typedoc'; + +/** + * The component that defines how to render a markdown document + * page. Rendering documents other than the README use this template. + * + * @param context - The theme context. + * @param props - A page event with the document's reflection. + * + * @returns A JSX.Element to render the markdown document. + */ +export const documentTemplate = ( + context: DefaultThemeRenderContext, + props: PageEvent +): JSX.Element => ( +
      + +
      +); diff --git a/src/Theme/Templates/hierarchyTemplate.tsx b/src/Theme/Templates/hierarchyTemplate.tsx new file mode 100755 index 0000000..1421a17 --- /dev/null +++ b/src/Theme/Templates/hierarchyTemplate.tsx @@ -0,0 +1,86 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * @module Theme/Templates + * @author Alan Rodas Bonjour + */ + +import { JSX } from 'typedoc'; +import type { DeclarationReflection, DefaultThemeRenderContext, PageEvent, ProjectReflection } from 'typedoc'; + +import { getHierarchyRoots } from '../../Utils/lib'; + +/** + * The component that defines how to render an element's hierarchy. + * The page that displays the hierarchy of an element as main content + * uses this template. + * + * @param context - The theme context. + * @param props - A page event with the project's reflection. + * + * @returns A JSX.Element to render the hierarchy. + */ +export const hierarchyTemplate = ( + context: DefaultThemeRenderContext, + props: PageEvent +): JSX.Element => ( + <> +

      {context.i18n.theme_class_hierarchy_title()}

      + {getHierarchyRoots(props.project).map((root) => ( +
        {fullHierarchy(context, root)}
      + ))} + +); + +/** + * The component that defines how to render an element's hierarchy + * from a particular root, recursively. + * + * @param context - The theme context. + * @param root - The main reflation from which to start the recursion. + * @param seen - A set containing all previously seen elements. + * + * @returns A JSX.Element to render the hierarchy of this element. + */ +const fullHierarchy = ( + context: DefaultThemeRenderContext, + root: DeclarationReflection, + seen = new Set() +): JSX.Element | undefined => { + if (seen.has(root)) return; + seen.add(root); + + // Note: We don't use root.anchor for the anchor, because those are built on a per page basis. + // And classes/interfaces get their own page, so all the anchors will be empty anyways. + // Full name should be safe here, since this list only includes classes/interfaces. + return ( +
    • + + + {context.icons[root.kind]()} + {root.name} + +
        + {root.implementedBy?.map( + (child) => + child.reflection && fullHierarchy(context, child.reflection as DeclarationReflection, seen) + )} + {root.extendedBy?.map( + (child) => + child.reflection && fullHierarchy(context, child.reflection as DeclarationReflection, seen) + )} +
      +
    • + ); +}; diff --git a/src/Theme/Templates/index.ts b/src/Theme/Templates/index.ts new file mode 100644 index 0000000..28b06a0 --- /dev/null +++ b/src/Theme/Templates/index.ts @@ -0,0 +1,28 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This sub-module exposes the main templates of the theme. + * A template is an organization for the main elements of the + * page, that is, the structure of the main content, which varies + * depending on the content type. + * + * @module Theme/Templates + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './documentTemplate'; +export * from './hierarchyTemplate'; +export * from './indexTemplate'; +export * from './reflectionTemplate'; diff --git a/src/Theme/Templates/indexTemplate.tsx b/src/Theme/Templates/indexTemplate.tsx new file mode 100755 index 0000000..a70e9d4 --- /dev/null +++ b/src/Theme/Templates/indexTemplate.tsx @@ -0,0 +1,34 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * @module Theme/Templates + * @author Alan Rodas Bonjour + */ + +import type { DefaultThemeRenderContext, PageEvent, ProjectReflection } from 'typedoc'; +import { JSX } from 'typedoc'; + +/** + * The component that defines how to render the main README file. + * + * @param context - The theme context. + * @param props - A page event with the project's reflection. + * + * @returns A JSX.Element to render the README file. + */ +export const indexTemplate = (context: DefaultThemeRenderContext, props: PageEvent): JSX.Element => ( +
      + +
      +); diff --git a/src/templates/reflection.tsx b/src/Theme/Templates/reflectionTemplate.tsx similarity index 72% rename from src/templates/reflection.tsx rename to src/Theme/Templates/reflectionTemplate.tsx index 375e04f..c9fc0f5 100755 --- a/src/templates/reflection.tsx +++ b/src/Theme/Templates/reflectionTemplate.tsx @@ -10,22 +10,41 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * @module Theme/Templates + * @author Alan Rodas Bonjour + */ + import { ContainerReflection, + DeclarationReflection, DefaultThemeRenderContext, + JSX, PageEvent, - DeclarationReflection, ReflectionKind, ReflectionType, - SignatureReflection, - JSX + SignatureReflection } from 'typedoc'; -import { classNames, getKindClass, hasTypeParameters } from '../partials/lib'; +import { classNames, getKindClass, hasTypeParameters } from '../../Utils/lib'; -export function reflectionTemplate(context: DefaultThemeRenderContext, props: PageEvent) { +/** + * The component that defines how to render any reflection element. + * Any type of element to display, such as a module, class, function or other + * uses this template as the main structure. + * + * @param context - The theme context. + * @param props - A page event with the container's reflection. + * + * @returns A JSX.Element to render the reflection element. + */ +export const reflectionTemplate = ( + context: DefaultThemeRenderContext, + props: PageEvent +): JSX.Element => { if ( - props.model.kindOf(ReflectionKind.TypeAlias | ReflectionKind.Variable) && + props.model.kindOf(ReflectionKind.TypeAlias || ReflectionKind.Variable) && props.model instanceof DeclarationReflection ) { return context.memberDeclaration(props.model); @@ -92,24 +111,22 @@ export function reflectionTemplate(context: DefaultThemeRenderContext, props: Pa {context.members(props.model)} ); -} +}; -function renderIndexSignature(context: DefaultThemeRenderContext, index: SignatureReflection) { - return ( -
    • -
      - [ - {index.parameters?.map((item) => ( - <> - {item.name}: {context.type(item.type)} - - ))} - ]: - {context.type(index.type)} -
      - {context.commentSummary(index)} - {context.commentTags(index)} - {index.type instanceof ReflectionType && context.parameter(index.type.declaration)} -
    • - ); -} +const renderIndexSignature = (context: DefaultThemeRenderContext, index: SignatureReflection): JSX.Element => ( +
    • +
      + [ + {index.parameters?.map((item) => ( + <> + {item.name}:{context.type(item.type)} + + ))} + ]: + {context.type(index.type)} +
      + {context.commentSummary(index)} + {context.commentTags(index)} + {index.type instanceof ReflectionType && context.parameter(index.type.declaration)} +
    • +); diff --git a/src/Theme/index.ts b/src/Theme/index.ts new file mode 100644 index 0000000..b31cfaf --- /dev/null +++ b/src/Theme/index.ts @@ -0,0 +1,25 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * The Theme module comprises the main elements that make up the theme, + * including the `Theme` class and the `ThemeContext` class, as well + * as all submodules that comprise the styles and behavior of the theme. + * + * @module Theme + * @author Alan Rodas Bonjour + * + * @internal + */ +export * from './GobstonesTheme'; +export * from './GobstonesThemeContext'; diff --git a/src/Utils/Options.ts b/src/Utils/Options.ts new file mode 100644 index 0000000..f824d80 --- /dev/null +++ b/src/Utils/Options.ts @@ -0,0 +1,77 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * @module Utils + * @author Alan Rodas Bonjour + */ + +import { Application } from 'typedoc'; + +/** + * Hook a set of options to the application, both at the moment + * and just after the bootstrapping of the application has been performed. + * + * @param typedocApp - The instance of the running TypeDoc application + * @param optionValues - The options to set in the application. + */ +export const hookThemeDefaultOptions = (typedocApp: Application, optionValues: Record): void => { + // Do a first setup right now with the properties that can be set + setThemeDefaultOptions(typedocApp, optionValues); + // Then hook to the bootstrap end + typedocApp.on(Application.EVENT_BOOTSTRAP_END, () => { + setThemeDefaultOptions(typedocApp, optionValues); + }); +}; + +/** + * Set the given options that are not defined already into the application. + * + * @param typedocApp - The instance of the running TypeDoc application + * @param optionValues - The options to set in the application. + * + * @internal + */ +const setThemeDefaultOptions = (typedocApp: Application, optionValues: Record): void => { + for (const optionName of Object.keys(optionValues)) { + setOptionIfNotDefined(typedocApp, optionName, optionValues[optionName]); + } +}; + +/** + * Set a given option with the given value on the application if not previously defined. + * + * @param typedocApp - The instance of the running TypeDoc application + * @param option - The option name to set. + * @param value - The value to set the option to. + * + * @internal + */ +const setOptionIfNotDefined = (typedocApp: Application, option: string, value: unknown): void => { + let isDefined: boolean = false; + let isSet: boolean = false; + try { + // Note that we can not set all options defined, always, as some options + // only become available after some plugins have defined them. + // The isSet function throws if the option is not defined, thus, the + // try catch is required to verify if the option was or not defined + // before setting it. + isSet = typedocApp.options.isSet(option); + isDefined = true; + } catch { + isDefined = false; + } + + if (isDefined && !isSet) { + typedocApp.options.setValue(option, value); + } +}; diff --git a/src/Utils/Plugins.ts b/src/Utils/Plugins.ts new file mode 100644 index 0000000..59b315e --- /dev/null +++ b/src/Utils/Plugins.ts @@ -0,0 +1,58 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * @module Utils + * @author Alan Rodas Bonjour + */ + +import { Application } from 'typedoc'; + +/** + * A class that wraps the idea of a `Plugin` for TypeDoc. + * + * @remarks + * Similarly to themes, this class is structured so that + * subclasses can access the main TypeDoc app through + * the `this.application` property. + * + * Subclasses are expected to overwrite this class and to + * define an `initialize` method, which is just a fancy name + * for the `load` function that is exported by regular plugins. + */ +export abstract class TypedocPlugin { + /** + * Create a new instance of this plugin. + * + * @param application - The instance of the running TypeDoc application + */ + public constructor(public application: Application) {} + + /** + * Initialize the plugin, loading all required configuration for it. + * + * @remarks + * This function is called when the plugin is loaded, after instantiation + * by the {@link loadPlugin} function. + */ + public abstract initialize(): void; +} + +/** + * Load a plugin into the application. + * + * @param typedocApp - The instance of the running TypeDoc application + * @param plugin - The class of the plugin to load. + */ +export const loadPlugin = (typedocApp: Application, plugin: new (app: Application) => TypedocPlugin): void => { + new plugin(typedocApp).initialize(); +}; diff --git a/src/Utils/index.ts b/src/Utils/index.ts new file mode 100644 index 0000000..73a005f --- /dev/null +++ b/src/Utils/index.ts @@ -0,0 +1,28 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ +/** + * This module contains utility functions and elements used through the theme. + * + * It include's some helpers to load plugins and options, as well as a + * copy from the lib file used in default typedoc theme, that is + * not exposed with the library, with some minor changes. + * + * @module Utils + * @author Alan Rodas Bonjour + * + * @internal + */ + +export * from './lib'; +export * from './Options'; +export * from './Plugins'; diff --git a/src/partials/lib.tsx b/src/Utils/lib.tsx similarity index 78% rename from src/partials/lib.tsx rename to src/Utils/lib.tsx index e1c570c..e3cc124 100755 --- a/src/partials/lib.tsx +++ b/src/Utils/lib.tsx @@ -10,32 +10,33 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + /** - * This module contains utility functions used through the theme. - * It's a copy from the lib file used in default typedoc theme, that is - * not exposed with the library. + * @module Utils/lib + * @author Alan Rodas Bonjour */ + import { - DefaultThemeRenderContext, DeclarationReflection, + DefaultThemeRenderContext, + JSX, ProjectReflection, ReferenceReflection, Reflection, ReflectionKind, SignatureReflection, - TypeParameterReflection, - JSX + TypeParameterReflection } from 'typedoc'; /** * Turn any element into it's string form. */ -export function stringify(data: unknown) { +export const stringify = (data: unknown): string => { if (typeof data === 'bigint') { return data.toString() + 'n'; } return JSON.stringify(data); -} +}; /** * Get the full package name. @@ -43,25 +44,24 @@ export function stringify(data: unknown) { * @param refl * @returns */ -export function getDisplayName(refl: Reflection): string { +export const getDisplayName = (refl: Reflection): string => { let version = ''; if ((refl instanceof DeclarationReflection || refl instanceof ProjectReflection) && refl.packageVersion) { version = ` - v${refl.packageVersion}`; } return `${refl.name}${version}`; -} +}; -export function toStyleClass(str: string): string { - return str.replace(/(\w)([A-Z])/g, (_m, m1: string, m2: string) => m1 + '-' + m2).toLowerCase(); -} +export const toStyleClass = (str: string): string => + str.replace(/(\w)([A-Z])/g, (_m, m1: string, m2: string) => m1 + '-' + m2).toLowerCase(); -export function getKindClass(refl: Reflection): string { +export const getKindClass = (refl: Reflection): string => { if (refl instanceof ReferenceReflection) { return getKindClass(refl.getTargetReflectionDeep()); } return ReflectionKind.classString(refl.kind); -} +}; /** * Insert word break tags ```` into the given string. @@ -71,7 +71,7 @@ export function getKindClass(refl: Reflection): string { * @param str The string that should be split. * @return The original string containing ```` tags where possible. */ -export function wbr(str: string): (string | JSX.Element)[] { +export const wbr = (str: string): (string | JSX.Element)[] => { // TODO surely there is a better way to do this, but I'm tired. const ret: (string | JSX.Element)[] = []; const re = /[\s\S]*?(?:[^_-][_-](?=[^_-])|[^A-Z](?=[A-Z][^A-Z]))/g; @@ -84,9 +84,9 @@ export function wbr(str: string): (string | JSX.Element)[] { ret.push(str.slice(i)); return ret; -} +}; -export function join(joiner: JSX.Children, list: readonly T[], cb: (x: T) => JSX.Children) { +export const join = (joiner: JSX.Children, list: readonly T[], cb: (x: T) => JSX.Children): JSX.Element => { const result: JSX.Children = []; for (const item of list) { @@ -97,9 +97,12 @@ export function join(joiner: JSX.Children, list: readonly T[], cb: (x: T) => } return <>{result}; -} +}; -export function classNames(names: Record, extraCss?: string) { +export const classNames = ( + names: Record, + extraCss?: string +): string | undefined => { const css = Object.keys(names) .filter((key) => names[key]) .concat(extraCss || '') @@ -107,24 +110,21 @@ export function classNames(names: Record, ex .trim() .replace(/\s+/g, ' '); return css.length ? css : undefined; -} +}; -export function hasTypeParameters( +export const hasTypeParameters = ( reflection: Reflection -): reflection is Reflection & { typeParameters: TypeParameterReflection[] } { - return ( - (reflection instanceof DeclarationReflection || reflection instanceof SignatureReflection) && - reflection.typeParameters !== undefined && - // eslint-disable-next-line no-null/no-null - reflection.typeParameters !== null && - reflection.typeParameters.length > 0 - ); -} - -export function renderTypeParametersSignature( +): reflection is Reflection & { typeParameters: TypeParameterReflection[] } => + (reflection instanceof DeclarationReflection || reflection instanceof SignatureReflection) && + reflection.typeParameters !== undefined && + // eslint-disable-next-line no-null/no-null + reflection.typeParameters !== null && + reflection.typeParameters.length > 0; + +export const renderTypeParametersSignature = ( context: DefaultThemeRenderContext, typeParameters: readonly TypeParameterReflection[] | undefined -): JSX.Element { +): JSX.Element => { if (!typeParameters || typeParameters.length === 0) return <>; const hideParamTypes = context.options.getValue('hideParameterTypesInTitle'); @@ -165,20 +165,20 @@ export function renderTypeParametersSignature( {'>'} ); -} +}; /** * Renders the reflection name with an additional `?` if optional. */ -export function renderName(refl: Reflection) { +export const renderName = (refl: Reflection): JSX.Element | (string | JSX.Element)[] => { if (refl.flags.isOptional) { return <>{wbr(refl.name)}?; } return wbr(refl.name); -} +}; -export function getHierarchyRoots(project: ProjectReflection): DeclarationReflection[] { +export const getHierarchyRoots = (project: ProjectReflection): DeclarationReflection[] => { const allClasses = project.getReflectionsByKind(ReflectionKind.ClassOrInterface) as DeclarationReflection[]; const roots = allClasses.filter((refl) => { @@ -199,12 +199,10 @@ export function getHierarchyRoots(project: ProjectReflection): DeclarationReflec return types.every( (type) => !type.visit({ - reference(ref) { - return ref.reflection !== undefined; - } + reference: (ref) => ref.reflection !== undefined }) ); }); return roots.sort((a, b) => a.name.localeCompare(b.name)); -} +}; diff --git a/src/defaults.tsx b/src/defaults.tsx new file mode 100755 index 0000000..53f5e0d --- /dev/null +++ b/src/defaults.tsx @@ -0,0 +1,47 @@ +/* + * ***************************************************************************** + * Copyright (C) National University of Quilmes 2018-2024 + * Gobstones (TM) is a trademark of the National University of Quilmes. + * + * This program is free software distributed under the terms of the + * GNU Affero General Public License version 3. + * Additional terms added in compliance to section 7 of such license apply. + * + * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. + * ***************************************************************************** + */ + +/** + * @module Main + * @author Alan Rodas Bonjour + */ + +import { Application } from 'typedoc'; + +/** + * Return the theme's default configuration to be set, if no configuration + * that overwrites it is provided by the user. + * + * @param typedocApp - The instance of the running TypeDoc application + * + * @returns A TypeDoc partial configuration. + */ +export const getDefaults = (typedocApp: Application): Record => ({ + includeVersion: true, + categorizeByGroup: true, + excludeExternals: true, + excludeInternal: false, + excludePrivate: false, + hideGenerator: true, + disableSources: false, + githubPages: true, + excludeTags: ['@override', '@virtual', '@satisfies', '@overload'], + visibilityFilters: { + '@internal': false, + protected: false, + private: false, + inherited: false + }, + mergeModulesMergeMode: 'module-category', + modifierTags: ['@mergeTarget', ...typedocApp.options.getValue('modifierTags')] +}); diff --git a/src/index.tsx b/src/index.tsx index 8f0ec82..741b664 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -10,24 +10,50 @@ * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. * ***************************************************************************** */ + +/** + * The main module of the theme. + * + * @remarks + * To define a theme in TypeDoc, the main module need's to export only + * a `load` function. This is the module that exports such function. + * + * Additionally, this module provides the default options used by the theme. + * Note that not all the configuration can be provided by the theme, and + * some minimal configuration should be provided through the typedoc + * configuration file. + * + * @module Main + * @author Alan Rodas Bonjour + */ import { Application } from 'typedoc'; -import { GobstonesTheme } from './GobstonesTheme'; -import { loadPlugin } from './plugins'; -import { MdnLinksPlugin } from './plugins/MdnLinksPlugin'; -import { MergeModulePlugin } from './plugins/MergeModulePlugin'; -import { NotExportedPlugin } from './plugins/NotExportedPlugin'; -import { RemoveReferencesPlugin } from './plugins/RemoveReferencesPlugin'; +import { getDefaults } from './defaults'; +import { MdnLinksPlugin, MergeModulePlugin, NotExportedPlugin, RemoveReferencesPlugin } from './Plugins'; +import { GobstonesTheme } from './Theme/GobstonesTheme'; +import * as Options from './Utils/Options'; +import * as Plugins from './Utils/Plugins'; /** - * Called by TypeDoc when loading this theme as a plugin + * The **load** function is called by TypeDoc when loading the theme + * (as a plugin). This function performs all side-loads + * and uses the received `app` to define a new theme, + * with a given name and associated with a particular theme class. + * + * @param typedocApp - The instance of the running TypeDoc application */ -export function load(app: Application) { +export const load = (typedocApp: Application): void => { + // Hook the default options so they are loaded + // if no option was overwritten by the user configuration + // This needs to come first than any other action. + Options.hookThemeDefaultOptions(typedocApp, getDefaults(typedocApp)); + // Included plugins - loadPlugin(app, NotExportedPlugin); - loadPlugin(app, MergeModulePlugin); - loadPlugin(app, RemoveReferencesPlugin); - loadPlugin(app, MdnLinksPlugin); - // Main theme - app.renderer.defineTheme('gobstones', GobstonesTheme); -} + Plugins.loadPlugin(typedocApp, NotExportedPlugin); + Plugins.loadPlugin(typedocApp, MergeModulePlugin); + Plugins.loadPlugin(typedocApp, RemoveReferencesPlugin); + Plugins.loadPlugin(typedocApp, MdnLinksPlugin); + + // Include theme + typedocApp.renderer.defineTheme('gobstones', GobstonesTheme); +}; diff --git a/src/partials/navigation/sidebar/navigation.tsx b/src/partials/navigation/sidebar/navigation.tsx deleted file mode 100644 index 4c658a4..0000000 --- a/src/partials/navigation/sidebar/navigation.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) National University of Quilmes 2018-2024 - * Gobstones (TM) is a trademark of the National University of Quilmes. - * - * This program is free software distributed under the terms of the - * GNU Affero General Public License version 3. - * Additional terms added in compliance to section 7 of such license apply. - * - * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. - * ***************************************************************************** - */ -import { Reflection, JSX, ReflectionKind, PageEvent, DefaultThemeRenderContext } from 'typedoc'; - -import { classNames } from '../../lib'; - -export const navigation = function navigation(context: DefaultThemeRenderContext, props: PageEvent) { - return ( - - ); -}; diff --git a/src/partials/types/typeParameters.tsx b/src/partials/types/typeParameters.tsx deleted file mode 100755 index 15947a0..0000000 --- a/src/partials/types/typeParameters.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) National University of Quilmes 2018-2024 - * Gobstones (TM) is a trademark of the National University of Quilmes. - * - * This program is free software distributed under the terms of the - * GNU Affero General Public License version 3. - * Additional terms added in compliance to section 7 of such license apply. - * - * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. - * ***************************************************************************** - */ -import type { DefaultThemeRenderContext, TypeParameterReflection } from 'typedoc'; -import { JSX } from 'typedoc'; - -export function typeParameters(context: DefaultThemeRenderContext, typeParameters: TypeParameterReflection[]) { - return ( - <> -
      -

      {context.i18n.kind_plural_type_parameter()}

      -
        - {typeParameters.map((item) => ( -
      • - - - {item.flags.isConst && const } - {item.varianceModifier && ( - {item.varianceModifier} - )} - {item.name} - {!!item.type && ( - <> - extends - {context.type(item.type)} - - )} - {!!item.default && ( - <> - {' = '} - {context.type(item.default)} - - )} - - {context.commentSummary(item)} - {context.commentTags(item)} -
      • - ))} -
      -
      - - ); -} diff --git a/src/static/assets/css/typedoc-theme-gobstones.css b/src/static/assets/css/typedoc-theme-gobstones.css index eed5ca1..b2c5d79 100755 --- a/src/static/assets/css/typedoc-theme-gobstones.css +++ b/src/static/assets/css/typedoc-theme-gobstones.css @@ -45,6 +45,16 @@ html { align-self: center; margin-right: 0.5rem; } +:root[data-theme='dark'] .tsd-icon-inverted { + -webkit-filter: invert(.75); /* safari 6.0 - 9.0 */ + filter: invert(.75); +} +@media(prefers-color-scheme: dark) { + :root[data-theme='os'] .tsd-icon-inverted { + -webkit-filter: invert(.75); /* safari 6.0 - 9.0 */ + filter: invert(.75); + } +} /* Improve headings */ .tsd-page-header .tsd-breadcrumb { diff --git a/src/static/assets/js/typedoc-theme-gobstones.js b/src/static/assets/js/typedoc-theme-gobstones.js index f06b954..3b3f0ed 100644 --- a/src/static/assets/js/typedoc-theme-gobstones.js +++ b/src/static/assets/js/typedoc-theme-gobstones.js @@ -1,5 +1,6 @@ +/** @module @ignore */ /* eslint-disable no-undef */ -document.addEventListener('DOMContentLoaded', function () { +document.addEventListener('DOMContentLoaded', () => { const privateRemarks = document.getElementById('Private Remarks'); if (!privateRemarks) return; const privateRemarksTitle = privateRemarks.parentElement; diff --git a/src/templates/hierarchy.tsx b/src/templates/hierarchy.tsx deleted file mode 100755 index 31e8199..0000000 --- a/src/templates/hierarchy.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) National University of Quilmes 2018-2024 - * Gobstones (TM) is a trademark of the National University of Quilmes. - * - * This program is free software distributed under the terms of the - * GNU Affero General Public License version 3. - * Additional terms added in compliance to section 7 of such license apply. - * - * You may read the full license at https://gobstones.github.io/gobstones-guidelines/LICENSE. - * ***************************************************************************** - */ -import { JSX } from 'typedoc'; -import type { DefaultThemeRenderContext, PageEvent, DeclarationReflection, ProjectReflection } from 'typedoc'; - -import { getHierarchyRoots } from '../partials/lib'; - -function fullHierarchy( - context: DefaultThemeRenderContext, - root: DeclarationReflection, - seen = new Set() -) { - if (seen.has(root)) return; - seen.add(root); - - // Note: We don't use root.anchor for the anchor, because those are built on a per page basis. - // And classes/interfaces get their own page, so all the anchors will be empty anyways. - // Full name should be safe here, since this list only includes classes/interfaces. - return ( -
    • - - - {context.icons[root.kind]()} - {root.name} - -
        - {root.implementedBy?.map((child) => { - return child.reflection && fullHierarchy(context, child.reflection as DeclarationReflection, seen); - })} - {root.extendedBy?.map((child) => { - return child.reflection && fullHierarchy(context, child.reflection as DeclarationReflection, seen); - })} -
      -
    • - ); -} - -export function hierarchyTemplate(context: DefaultThemeRenderContext, props: PageEvent) { - return ( - <> -

      {context.i18n.theme_class_hierarchy_title()}

      - {getHierarchyRoots(props.project).map((root) => ( -
        {fullHierarchy(context, root)}
      - ))} - - ); -} diff --git a/tsconfig.json b/tsconfig.json old mode 100755 new mode 100644 index 755477f..00979ae --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,12 @@ { - "compilerOptions": { - // "allowSyntheticDefaultImports": true, - // "alwaysStrict": true, - "baseUrl": "src", + "compilerOptions": { + "baseUrl": "src", "outDir": "./dist", "target": "ES2022", "strict": true, - "esModuleInterop": true, + "esModuleInterop": true, "allowJs": true, - "sourceMap": true, + "sourceMap": false, "declaration": true, "declarationMap": true, "declarationDir": "./dist/typings", @@ -17,22 +15,11 @@ "composite": false, "skipLibCheck": true, "strictNullChecks": true, - // "forceConsistentCasingInFileNames": true, - "jsx": "react", - "jsxFactory": "JSX.createElement", - "jsxFragmentFactory": "JSX.Fragment", - "module": "CommonJS", - "moduleResolution": "Node" - // "noFallthroughCasesInSwitch": true, - // "noImplicitAny": true, - // "noImplicitReturns": true, - // "noImplicitThis": true, - // "noUnusedLocals": false, - // "noUnusedParameters": false, - // "strictBindCallApply": true, - // "strictFunctionTypes": true, - // "strictNullChecks": true, - // "strictPropertyInitialization": true, - }, - "include": ["src/**/*", "./src/@types/*.d.ts"] + "jsx": "react", + "jsxFactory": "JSX.createElement", + "jsxFragmentFactory": "JSX.Fragment", + "module": "CommonJS", + "moduleResolution": "Node" + }, + "include": ["src/**/*", "./src/@types/*.d.ts"] } diff --git a/typedoc.config.mjs b/typedoc.config.mjs new file mode 100644 index 0000000..32c688b --- /dev/null +++ b/typedoc.config.mjs @@ -0,0 +1,15 @@ +export default { + entryPoints: ['./src'], + entryPointStrategy: 'expand', + tsconfig: './tsconfig.json', + compilerOptions: { + rootDir: './src' + }, + out: './docs', + exclude: ['./node_modules/**/*', './**/*.test.ts', './src/index.ts'], + plugin: ['@gobstones/typedoc-theme-gobstones'], + theme: 'gobstones', + excludeExternals: true, + excludeInternal: false, + excludePrivate: false +};