├── .nvmrc
├── packages
├── docs
│ ├── yarn.lock
│ ├── static
│ │ ├── favicon.ico
│ │ ├── media
│ │ │ ├── hero.jpg
│ │ │ ├── concepts.png
│ │ │ ├── kicker.jpg
│ │ │ └── extensions
│ │ │ │ ├── khr-materials-sheen.png
│ │ │ │ ├── khr-materials-unlit.png
│ │ │ │ ├── khr-materials-variants.jpg
│ │ │ │ ├── khr-materials-volume.png
│ │ │ │ ├── khr-materials-clearcoat.png
│ │ │ │ └── khr-material-pbr-specular-glossiness.png
│ │ └── main.css
│ ├── src
│ │ ├── routes
│ │ │ ├── installation
│ │ │ │ └── +page.svx
│ │ │ ├── +page.svelte
│ │ │ ├── +page.server.ts
│ │ │ ├── modules
│ │ │ │ └── [module]
│ │ │ │ │ └── [kind]
│ │ │ │ │ └── [slug]
│ │ │ │ │ ├── +page.server.ts
│ │ │ │ │ └── +page.svelte
│ │ │ ├── +layout.server.ts
│ │ │ └── +layout.svelte
│ │ ├── app.d.ts
│ │ ├── app.html
│ │ └── lib
│ │ │ └── server
│ │ │ └── model
│ │ │ └── index.ts
│ ├── README.md
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc
│ ├── vite.config.ts
│ ├── tsconfig.json
│ ├── svelte.config.js
│ └── package.json
├── parse
│ ├── yarn.lock
│ ├── .gitignore
│ ├── src
│ │ ├── index.ts
│ │ ├── utils
│ │ │ ├── format.ts
│ │ │ ├── sort.ts
│ │ │ └── no-case.ts
│ │ ├── constants.ts
│ │ ├── types.ts
│ │ ├── Parser.ts
│ │ └── Encoder.ts
│ ├── tsconfig.json
│ ├── README.md
│ ├── package.json
│ └── test
│ │ └── index.test.ts
├── svelte
│ ├── .npmrc
│ ├── static
│ │ └── favicon.png
│ ├── README.md
│ ├── .gitignore
│ ├── src
│ │ ├── lib
│ │ │ ├── Comment.svelte
│ │ │ ├── Reference.svelte
│ │ │ ├── index.ts
│ │ │ ├── Sources.svelte
│ │ │ ├── Enum.svelte
│ │ │ ├── Tags.svelte
│ │ │ ├── EnumMember.svelte
│ │ │ ├── Property.svelte
│ │ │ ├── Interface.svelte
│ │ │ ├── Function.svelte
│ │ │ ├── Method.svelte
│ │ │ ├── Constructor.svelte
│ │ │ └── Class.svelte
│ │ ├── app.html
│ │ ├── app.d.ts
│ │ └── routes
│ │ │ └── +page.svelte
│ ├── vite.config.ts
│ ├── svelte.config.js
│ ├── tsconfig.json
│ └── package.json
└── README.md
├── .npmrc
├── .gitignore
├── .github
├── FUNDING.yml
└── workflows
│ └── ci.yml
├── lerna.json
├── .prettierrc
├── .prettierignore
├── renovate.json
├── nx.json
├── README.md
├── package.json
└── LICENSE.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/packages/docs/yarn.lock:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/parse/yarn.lock:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .nx/cache
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [donmccurdy]
2 |
--------------------------------------------------------------------------------
/packages/svelte/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/packages/parse/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
4 |
--------------------------------------------------------------------------------
/packages/docs/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/favicon.ico
--------------------------------------------------------------------------------
/packages/svelte/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/svelte/static/favicon.png
--------------------------------------------------------------------------------
/packages/docs/static/media/hero.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/hero.jpg
--------------------------------------------------------------------------------
/packages/docs/static/media/concepts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/concepts.png
--------------------------------------------------------------------------------
/packages/docs/static/media/kicker.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/kicker.jpg
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json",
3 | "npmClient": "yarn",
4 | "version": "0.4.1"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/docs/src/routes/installation/+page.svx:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | Lorem ipsum dolorem...
4 |
5 | ```html
6 | lorem ipsum
7 | ```
--------------------------------------------------------------------------------
/packages/parse/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Parser';
2 | export * from './Encoder';
3 | export * from './types';
4 | export * from './utils/sort';
5 |
--------------------------------------------------------------------------------
/packages/docs/README.md:
--------------------------------------------------------------------------------
1 | # 🌿 @greendoc/docs
2 |
3 | > WORK IN PROGRESS / NOT READY FOR USE
4 |
5 | _Documentation for, and example of, the greendoc system._
6 |
--------------------------------------------------------------------------------
/packages/docs/static/media/extensions/khr-materials-sheen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/extensions/khr-materials-sheen.png
--------------------------------------------------------------------------------
/packages/docs/static/media/extensions/khr-materials-unlit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/extensions/khr-materials-unlit.png
--------------------------------------------------------------------------------
/packages/svelte/README.md:
--------------------------------------------------------------------------------
1 | # 🌿 @greendoc/svelte
2 |
3 | > WORK IN PROGRESS / NOT READY FOR USE
4 |
5 | _Renders the API model in HTML, using [Svelte](https://svelte.dev/)._
6 |
--------------------------------------------------------------------------------
/packages/docs/static/media/extensions/khr-materials-variants.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/extensions/khr-materials-variants.jpg
--------------------------------------------------------------------------------
/packages/docs/static/media/extensions/khr-materials-volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/extensions/khr-materials-volume.png
--------------------------------------------------------------------------------
/packages/parse/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["ES2022"],
4 | "module": "ES2022",
5 | "moduleResolution": "node",
6 | "target": "ES2022"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/docs/static/media/extensions/khr-materials-clearcoat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/extensions/khr-materials-clearcoat.png
--------------------------------------------------------------------------------
/packages/docs/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 | vite.config.js.timestamp-*
10 | vite.config.ts.timestamp-*
11 |
--------------------------------------------------------------------------------
/packages/svelte/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /dist
6 | .env
7 | .env.*
8 | !.env.example
9 | vite.config.js.timestamp-*
10 | vite.config.ts.timestamp-*
11 |
--------------------------------------------------------------------------------
/packages/docs/static/media/extensions/khr-material-pbr-specular-glossiness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/greendoc/main/packages/docs/static/media/extensions/khr-material-pbr-specular-glossiness.png
--------------------------------------------------------------------------------
/packages/parse/README.md:
--------------------------------------------------------------------------------
1 | # 🌿 @greendoc/parse
2 |
3 | > WORK IN PROGRESS / NOT READY FOR USE
4 |
5 | _Parses TS/JS (with [API Extractor](https://api-extractor.com/)) and builds a strongly-typed API model in JSON._
6 |
--------------------------------------------------------------------------------
/packages/docs/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
🌿 greendoc
6 |
7 | An adaptable system for generating documentation of TypeScript APIs.
8 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Comment.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-svelte"],
7 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/docs/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 |
10 | # Ignore files for PNPM, NPM and YARN
11 | pnpm-lock.yaml
12 | package-lock.json
13 | yarn.lock
14 |
--------------------------------------------------------------------------------
/packages/docs/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-svelte"],
7 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/docs/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import type { UserConfig } from 'vite';
3 |
4 | const config: UserConfig = {
5 | plugins: [sveltekit()],
6 | server: { port: 4000 }
7 | };
8 |
9 | export default config;
10 |
--------------------------------------------------------------------------------
/packages/svelte/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import type { UserConfig } from 'vite';
3 |
4 | const config: UserConfig = {
5 | plugins: [sveltekit()],
6 | server: { port: 4001 }
7 | };
8 |
9 | export default config;
10 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | packages/*/build
4 | packages/*/dist
5 | packages/*/.svelte-kit
6 | .env
7 | .env.*
8 | !.env.example
9 |
10 | # Ignore files for PNPM, NPM and YARN
11 | pnpm-lock.yaml
12 | package-lock.json
13 | yarn.lock
14 |
15 | /.nx/cache
--------------------------------------------------------------------------------
/packages/docs/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://kit.svelte.dev/docs/types#app
2 | // for information about these interfaces
3 | // and what to do when importing types
4 | declare namespace App {
5 | // interface Locals {}
6 | // interface PageData {}
7 | // interface Error {}
8 | // interface Platform {}
9 | }
10 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["github>donmccurdy/renovate-config"],
3 | "packageRules": [
4 | {
5 | "description": "TypeScript support missing in marked-highlight. Also see https://github.com/donmccurdy/greendoc/issues/19.",
6 | "matchPackageNames": ["marked"],
7 | "enabled": false
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/docs/src/routes/+page.server.ts:
--------------------------------------------------------------------------------
1 | import type { PageServerLoad } from './$types';
2 |
3 | export const load: PageServerLoad = async ({ params }) => {
4 | return {
5 | metadata: {
6 | title: 'greendoc',
7 | snippet: 'An adaptable system for generating documentation of TypeScript APIs.'
8 | }
9 | };
10 | };
11 |
--------------------------------------------------------------------------------
/packages/svelte/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-auto';
2 | import preprocess from 'svelte-preprocess';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | preprocess: preprocess(),
7 |
8 | kit: {
9 | adapter: adapter()
10 | }
11 | };
12 |
13 | export default config;
14 |
--------------------------------------------------------------------------------
/packages/svelte/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/svelte/src/app.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | // See https://kit.svelte.dev/docs/types#app
4 | // for information about these interfaces
5 | // and what to do when importing types
6 | declare namespace App {
7 | // interface Locals {}
8 | // interface PageData {}
9 | // interface Error {}
10 | // interface Platform {}
11 | }
12 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Reference.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | {#if data.path}
8 | {data.name}
9 | {:else}
10 | {data.name}
11 | {/if}
12 |
13 |
--------------------------------------------------------------------------------
/packages/parse/src/utils/format.ts:
--------------------------------------------------------------------------------
1 | import { marked } from 'marked';
2 | import hljs from 'highlight.js';
3 |
4 | marked.setOptions({
5 | highlight: (code, lang) => {
6 | const language = hljs.getLanguage(lang) ? lang : 'plaintext';
7 | return hljs.highlight(code, { language }).value;
8 | }
9 | });
10 |
11 | export function markedFormatter(md: string): string {
12 | return marked.parse(md);
13 | }
14 |
--------------------------------------------------------------------------------
/packages/parse/src/constants.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * JSDoc and TSDoc tags included in API serialization. Currently this list
3 | * is chosen to represent presentation-related tags, like alpha/beta/public
4 | * release status, rather than type-related tags.
5 | */
6 | export const SUPPORTED_TAGS = new Set([
7 | 'alpha',
8 | 'beta',
9 | 'experimental',
10 | 'public',
11 | 'deprecated',
12 | 'category' // mine
13 | ]);
14 |
--------------------------------------------------------------------------------
/packages/docs/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | %sveltekit.head%
10 |
11 |
12 |
13 |
14 | %sveltekit.body%
15 |
16 |
17 |
--------------------------------------------------------------------------------
/packages/README.md:
--------------------------------------------------------------------------------
1 | # 🌿 greendoc → packages
2 |
3 | > WORK IN PROGRESS / NOT READY FOR USE
4 |
5 | _An adaptable system for generating documentation of TypeScript and JavaScript APIs._
6 |
7 | ## Packages
8 |
9 | - [`@greendoc/docs`](./docs): Documentation for, and example of, the greendoc system
10 | - [`@greendoc/parse`](./parse): Parses TS/JS (with [TS Morph](https://ts-morph.com/)) and builds a strongly-typed API model in JSON
11 | - [`@greendoc/svelte`](./svelte): Renders the API model in HTML, using [Svelte](https://svelte.dev/)
12 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "targetDefaults": {
3 | "build": {
4 | "dependsOn": ["^build"],
5 | "outputs": ["{projectRoot}/dist", "{projectRoot}/build"],
6 | "cache": true
7 | },
8 | "check": {
9 | "cache": true
10 | },
11 | "check:watch": {
12 | "cache": true
13 | },
14 | "lint": {
15 | "cache": true
16 | },
17 | "format": {
18 | "cache": true
19 | },
20 | "test": {
21 | "cache": true
22 | }
23 | },
24 | "namedInputs": {
25 | "default": ["{projectRoot}/**/*", "sharedGlobals"],
26 | "sharedGlobals": [],
27 | "production": ["default"]
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true
12 | }
13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
14 | //
15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
16 | // from the referenced tsconfig.json - TypeScript does not merge them in
17 | }
18 |
--------------------------------------------------------------------------------
/packages/svelte/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true
12 | }
13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
14 | //
15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
16 | // from the referenced tsconfig.json - TypeScript does not merge them in
17 | }
18 |
--------------------------------------------------------------------------------
/packages/docs/src/routes/modules/[module]/[kind]/[slug]/+page.server.ts:
--------------------------------------------------------------------------------
1 | import { error } from '@sveltejs/kit';
2 | import type { PageServerLoad } from './$types';
3 | import { parser, encoder, getMetadata } from '$lib/server/model';
4 | import type { GD } from '@greendoc/parse';
5 |
6 | export const load: PageServerLoad<{ export: GD.ApiItem }> = async ({ params }) => {
7 | const slug = params.slug.replace(/\.html$/, '');
8 | const item = parser.getItemBySlug(slug);
9 | const encodedItem = encoder.encodeItem(item);
10 | if (item && encodedItem) {
11 | return {
12 | metadata: getMetadata(encodedItem),
13 | export: encodedItem
14 | };
15 | }
16 | error(404, 'Not found');
17 | };
18 |
--------------------------------------------------------------------------------
/packages/docs/svelte.config.js:
--------------------------------------------------------------------------------
1 | // import adapter from '@sveltejs/adapter-auto';
2 | import adapter from '@sveltejs/adapter-static';
3 | import preprocess from 'svelte-preprocess';
4 | import { mdsvex } from 'mdsvex';
5 |
6 | /** @type {import('@sveltejs/kit').Config} */
7 | const config = {
8 | extensions: ['.svelte', '.md', '.svx'],
9 |
10 | // Consult https://github.com/sveltejs/svelte-preprocess
11 | // for more information about preprocessors
12 | preprocess: [preprocess(), mdsvex({ extensions: ['.md', '.svx'] })],
13 |
14 | kit: {
15 | adapter: adapter(),
16 | prerender: {
17 | handleHttpError: 'warn',
18 | handleMissingId: 'warn'
19 | }
20 | }
21 | };
22 |
23 | export default config;
24 |
--------------------------------------------------------------------------------
/packages/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@greendocs/docs",
3 | "version": "0.4.1",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite dev --force",
7 | "build": "vite build",
8 | "preview": "vite preview",
9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
11 | "lint": "prettier --check src",
12 | "format": "prettier --write src",
13 | "clean": "rimraf build/*"
14 | },
15 | "dependencies": {
16 | "@greendoc/parse": "^0.4.1",
17 | "@greendoc/svelte": "^0.4.1",
18 | "@types/he": "^1.2.3",
19 | "he": "^1.2.0",
20 | "ts-morph": "^23.0.0"
21 | },
22 | "type": "module"
23 | }
24 |
--------------------------------------------------------------------------------
/packages/svelte/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 | Welcome to your library project
7 | Create your package using @sveltejs/package and preview/showcase your work with SvelteKit
8 | Visit kit.svelte.dev to read the documentation
9 |
24 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | // Reexport your entry components here
2 | export { default as Class } from './Class.svelte';
3 | export { default as Constructor } from './Constructor.svelte';
4 | export { default as Comment } from './Comment.svelte';
5 | export { default as Enum } from './Enum.svelte';
6 | export { default as EnumMember } from './EnumMember.svelte';
7 | export { default as Function } from './Function.svelte';
8 | export { default as Interface } from './Interface.svelte';
9 | export { default as Method } from './Method.svelte';
10 | export { default as Property } from './Property.svelte';
11 | export { default as Reference } from './Reference.svelte';
12 | export { default as Sources } from './Sources.svelte';
13 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Sources.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
28 |
--------------------------------------------------------------------------------
/packages/docs/src/routes/modules/[module]/[kind]/[slug]/+page.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | {data.export.name}
9 |
10 |
11 | {#if data.export.kind === 'Class'}
12 |
13 | {:else if data.export.kind === 'Interface'}
14 |
15 | {:else if data.export.kind === 'Enum'}
16 |
17 | {:else if data.export.kind === 'Function'}
18 |
19 | {:else}
20 | Error: Unknown kind, "${data.export.kind}"
21 | {/if}
22 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Enum.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
17 |
18 | {#if data.members.length}
19 |
20 | Members
21 | {#each data.members as member}
22 |
23 | {/each}
24 |
25 | {/if}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Tags.svelte:
--------------------------------------------------------------------------------
1 |
22 |
23 | {#if hasVisibleTag()}
24 |
33 | {/if}
34 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/EnumMember.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 | {data.name}{#if typeof data.type === 'string'}:
15 | {data.type}{:else if data.type}: {/if}
18 |
19 | {#if data.tags}
20 |
21 | {/if}
22 | {#if data.comment}
23 |
24 | {/if}
25 |
26 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Property.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 | {data.name}{#if typeof data.type === 'string'}:
15 | {data.type}{:else if data.type}: {/if}
18 |
19 | {#if data.tags}
20 |
21 | {/if}
22 | {#if data.comment}
23 |
24 | {/if}
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 |
9 | jobs:
10 | build:
11 | strategy:
12 | matrix:
13 | node-version: [20.x]
14 | os: [ubuntu-latest, windows-latest]
15 | runs-on: ${{ matrix.os }}
16 | env:
17 | CI: true
18 | LINT: ${{ matrix.os == 'ubuntu-latest' && true || false }}
19 |
20 | steps:
21 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v4
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 |
27 | # https://github.com/yarnpkg/yarn/issues/4890
28 | - run: yarn install --frozen-lockfile
29 | if: matrix.os == 'ubuntu-latest'
30 | - run: yarn install --frozen-lockfile --network-timeout 100000
31 | if: matrix.os == 'windows-latest'
32 |
33 | - run: yarn build
34 | - run: yarn test
35 | - run: yarn lint
36 | if: env.LINT == 'true'
37 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Interface.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
18 |
19 | {#if data.properties.length}
20 |
21 | Properties
22 | {#each data.properties as property}
23 |
24 | {/each}
25 |
26 | {/if}
27 |
28 | {#if data.methods.length}
29 |
30 | Methods
31 | {#each data.methods as method}
32 |
33 | {/each}
34 |
35 | {/if}
36 |
37 |
38 |
--------------------------------------------------------------------------------
/packages/parse/src/utils/sort.ts:
--------------------------------------------------------------------------------
1 | import { noCase } from './no-case';
2 | import { GD } from '../types';
3 |
4 | export type SortFn = (a: GD.ApiItemBase, b: GD.ApiItemBase) => number;
5 |
6 | /**
7 | * Defines default sort order, strictly alphabetical.
8 | */
9 | export function createDefaultSort(): SortFn {
10 | return (a, b) => (a.name > b.name ? 1 : -1);
11 | }
12 |
13 | const DEFAULT_PREFIX_LIST = ['get', 'set', 'add', 'remove', 'delete', 'list', 'to', 'from'];
14 |
15 | /**
16 | * Defines a prefix-based sort order, using the provided prefix list. This
17 | * sorting method is designed to keep items like `getA` and `setA` next to one
18 | * another, sorting first with the prefix omitted from each item, and then
19 | * by prefix.
20 | */
21 | export function createPrefixSort(prefixList = DEFAULT_PREFIX_LIST): SortFn {
22 | const prefixSet = new Set(prefixList);
23 | return (a, b) => {
24 | const tokensA = noCase(a.name);
25 | const tokensB = noCase(b.name);
26 |
27 | const prefixA = prefixSet.has(tokensA[0]) ? tokensA.shift() : '';
28 | const prefixB = prefixSet.has(tokensB[0]) ? tokensB.shift() : '';
29 |
30 | const nameA = tokensA.join('');
31 | const nameB = tokensB.join('');
32 |
33 | return (nameA !== nameB ? nameA > nameB : prefixA > prefixB) ? 1 : -1;
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🌿 greendoc
2 |
3 | [](https://www.npmjs.com/package/@greendoc/parse)
4 | [](https://github.com/donmccurdy/greendoc/blob/main/LICENSE.md)
5 | [](https://github.com/donmccurdy/greendoc/actions?query=workflow%3ACI)
6 |
7 | _An adaptable system for generating documentation of TypeScript APIs._
8 |
9 | > ⚠️ **NOTICE:** WORK IN PROGRESS / NOT READY FOR USE
10 |
11 | ## Packages
12 |
13 | - [`@greendoc/docs`](./packages/docs): _Documentation for, and example of, the greendoc system._
14 | - [`@greendoc/parse`](./packages/parse): _Parses TypeScript and builds a strongly-typed API model in JSON._
15 | - `@greendoc/html`: _Renders the API model in HTML._
16 | - [`@greendoc/svelte`](./packages/svelte): _Renders the API model in HTML, using [Svelte](https://svelte.dev/)._
17 | - `@greendoc/react`: _Renders the API model in HTML, using [React](https://reactjs.org/)._
18 | - `@greendoc/i18n`: _Provides [internationalization (i18n)](https://web.dev/learn/design/internationalization/) support._
19 |
20 | ## License
21 |
22 | Licensed under [Blue Oak Model License 1.0.0](/LICENSE.md).
23 |
--------------------------------------------------------------------------------
/packages/parse/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@greendoc/parse",
3 | "version": "0.4.1",
4 | "type": "module",
5 | "sideEffects": false,
6 | "source": "./src/index.ts",
7 | "types": "./dist/index.d.ts",
8 | "main": "./dist/greendoc-parse.cjs",
9 | "module": "./dist/greendoc-parse.esm.js",
10 | "exports": {
11 | "types": "./dist/index.d.ts",
12 | "require": "./dist/greendoc-parse.cjs",
13 | "default": "./dist/greendoc-parse.modern.js"
14 | },
15 | "files": [
16 | "dist/"
17 | ],
18 | "repository": "https://github.com/donmccurdy/greendoc",
19 | "author": "Don McCurdy ",
20 | "license": "BlueOak-1.0.0",
21 | "scripts": {
22 | "build": "microbundle --format cjs,esm,modern --no-compress --define PACKAGE_VERSION=$npm_package_version",
23 | "test": "ava --no-worker-threads test/*.test.ts",
24 | "preversion": "yarn build && yarn test",
25 | "version": "rimraf dist/* && yarn build && git add -u",
26 | "postversion": "git push && git push --tags && npm publish",
27 | "clean": "rimraf dist/*"
28 | },
29 | "dependencies": {
30 | "highlight.js": "^11.9.0",
31 | "marked": "^4.2.3",
32 | "ts-morph": "^23.0.0"
33 | },
34 | "ava": {
35 | "extensions": {
36 | "ts": "module"
37 | },
38 | "nodeArguments": [
39 | "--import=tsx"
40 | ]
41 | },
42 | "gitHead": "276870f41d0e92060bb04b138d1ee8fbe643d1d3"
43 | }
44 |
--------------------------------------------------------------------------------
/packages/docs/src/lib/server/model/index.ts:
--------------------------------------------------------------------------------
1 | import { resolve, dirname } from 'node:path';
2 | import { fileURLToPath } from 'node:url';
3 | import { Encoder, GD, Parser } from '@greendoc/parse';
4 | import * as TS from 'ts-morph';
5 | import he from 'he';
6 |
7 | const ROOT_DELTA = '../../../../../../';
8 | const ROOT_PATH = resolve(dirname(fileURLToPath(import.meta.url)), ROOT_DELTA);
9 |
10 | const entryPath = resolve(ROOT_PATH, 'packages/parse/src/index.ts');
11 |
12 | const project = new TS.Project({
13 | compilerOptions: {
14 | paths: {
15 | '@greendoc/parse': [entryPath]
16 | }
17 | }
18 | });
19 |
20 | export const parser = new Parser(project)
21 | .addModule({ name: '@greendoc/parse', slug: 'parse', entry: entryPath })
22 | .setRootPath(ROOT_PATH)
23 | .setBaseURL('https://github.com/donmccurdy/greendoc/tree/main')
24 | .init();
25 |
26 | export const encoder = new Encoder(parser);
27 |
28 | export function getMetadata(item: GD.ApiItem): {
29 | title: string;
30 | snippet: string;
31 | } {
32 | return {
33 | title: item.name,
34 | snippet: (item as any).comment ? getSnippet((item as any).comment) : ''
35 | };
36 | }
37 |
38 | export function getSnippet(html: string): string {
39 | const text = he.decode(html.replace(/(<([^>]+)>)/gi, ''));
40 | const words = text.split(/\s+/);
41 | if (words.length < 50) return text;
42 | return words.slice(0, 50).join(' ') + '…';
43 | }
44 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Function.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 | -
15 | {data.name}({#each data.params as param, i}{param.name}{#if param.optional}?{/if}{#if typeof param.type === 'string'}:
16 | {param.type}{:else if param.type}: {/if}{#if i < data.params.length - 1},
19 | {/if}{/each}):
20 | {#if typeof data.returns === 'string'}{data.returns}{:else}{/if}
23 |
24 |
25 | {#if data.tags}
26 |
27 | {/if}
28 |
29 | -
30 | {#if data.comment}
31 |
32 | {/if}
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/packages/parse/src/utils/no-case.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * no-case
3 | *
4 | * Based on https://github.com/blakeembrey/change-case/blob/master/packages/no-case/src/index.ts,
5 | * released under MIT License.
6 | */
7 |
8 | // Support camel case ("camelCase" -> "camel Case" and "CAMELCase" -> "CAMEL Case").
9 | const DEFAULT_SPLIT_REGEXP = [/([a-z0-9])([A-Z])/g, /([A-Z])([A-Z][a-z])/g];
10 |
11 | // Remove all non-word characters.
12 | const DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi;
13 |
14 | /** Normalize the string into an array of 'words'. */
15 | export function noCase(input: string): string[] {
16 | const splitRegexp = DEFAULT_SPLIT_REGEXP;
17 | const stripRegexp = DEFAULT_STRIP_REGEXP;
18 | const transform = (s: string) => s.toLowerCase();
19 | const delimiter = ' ';
20 |
21 | let result = replace(replace(input, splitRegexp, '$1\0$2'), stripRegexp, '\0');
22 | let start = 0;
23 | let end = result.length;
24 |
25 | // Trim the delimiter from around the output string.
26 | while (result.charAt(start) === '\0') start++;
27 | while (result.charAt(end - 1) === '\0') end--;
28 |
29 | // Transform each token independently.
30 | return result.slice(start, end).split('\0').map(transform);
31 | }
32 |
33 | /** Replace `re` in the input string with the replacement value. */
34 | function replace(input: string, re: RegExp | RegExp[], value: string) {
35 | if (re instanceof RegExp) return input.replace(re, value);
36 | return re.reduce((input, re) => input.replace(re, value), input);
37 | }
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "root",
3 | "private": true,
4 | "type": "module",
5 | "workspaces": [
6 | "packages/*"
7 | ],
8 | "scripts": {
9 | "dev": "concurrently 'yarn build:watch' 'lerna run dev'",
10 | "build": "lerna run build",
11 | "build:watch": "lerna watch --scope=$LERNA_PACKAGE_NAME --include-dependents --no-private -- lerna run build",
12 | "test": "lerna run test",
13 | "test:watch": "lerna watch --scope=$LERNA_PACKAGE_NAME -- lerna run test",
14 | "clean": "lerna run clean",
15 | "lint": "lerna run lint",
16 | "preversion": "yarn clean && yarn build && yarn test",
17 | "postpublish": "git push && git push --tags"
18 | },
19 | "devDependencies": {
20 | "@sveltejs/adapter-auto": "^3.0.0",
21 | "@sveltejs/adapter-static": "^3.0.0",
22 | "@sveltejs/kit": "^2.0.0",
23 | "@sveltejs/package": "^2.3.1",
24 | "@sveltejs/vite-plugin-svelte": "^3.0.0",
25 | "@types/node": "^22.0.0",
26 | "ava": "^6.1.2",
27 | "concurrently": "^9.0.0",
28 | "lerna": "^8.1.2",
29 | "mdsvex": "^0.12.0",
30 | "microbundle": "^0.15.1",
31 | "prettier": "^3.2.5",
32 | "prettier-plugin-svelte": "^3.2.3",
33 | "rimraf": "^6.0.0",
34 | "svelte": "^4.2.15",
35 | "svelte-check": "^4.0.0",
36 | "svelte-preprocess": "^6.0.0",
37 | "ts-morph": "^23.0.0",
38 | "tsx": "^4.7.3",
39 | "typescript": "^5.4.5",
40 | "vite": "^5.2.10"
41 | },
42 | "ava": {
43 | "extensions": {
44 | "ts": "module"
45 | },
46 | "nodeArguments": [
47 | "--import=tsx"
48 | ]
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Method.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
14 |
15 |
16 |
20 | -
21 | {data.name}({#each data.params as param, i}{param.name}{#if param.optional}?{/if}{#if typeof param.type === 'string'}:
22 | {param.type}{:else if param.type}: {/if}{#if i < data.params.length - 1},
25 | {/if}{/each}):
26 | {#if typeof data.returns === 'string'}{data.returns}{:else}{/if}
29 |
30 |
31 | {#if data.tags}
32 |
33 | {/if}
34 |
35 | -
36 | {#if data.comment}
37 |
38 | {/if}
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Constructor.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
14 |
15 |
16 |
20 | -
21 | {data.name}({#each data.params as param, i}{param.name}{#if param.optional}?{/if}{#if typeof param.type === 'string'}:
22 | {param.type}{:else if param.type}: {/if}{#if i < data.params.length - 1},
25 | {/if}{/each}):
26 | {#if typeof data.returns === 'string'}{data.returns}{:else}{/if}
29 |
30 |
31 | {#if data.tags}
32 |
33 | {/if}
34 |
35 | -
36 | {#if data.comment}
37 |
38 | {/if}
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Blue Oak Model License
2 |
3 | Version 1.0.0
4 |
5 | ## Purpose
6 |
7 | This license gives everyone as much permission to work with
8 | this software as possible, while protecting contributors
9 | from liability.
10 |
11 | ## Acceptance
12 |
13 | In order to receive this license, you must agree to its
14 | rules. The rules of this license are both obligations
15 | under that agreement and conditions to your license.
16 | You must not do anything with this software that triggers
17 | a rule that you cannot or will not follow.
18 |
19 | ## Copyright
20 |
21 | Each contributor licenses you to do everything with this
22 | software that would otherwise infringe that contributor's
23 | copyright in it.
24 |
25 | ## Notices
26 |
27 | You must ensure that everyone who gets a copy of
28 | any part of this software from you, with or without
29 | changes, also gets the text of this license or a link to
30 | .
31 |
32 | ## Excuse
33 |
34 | If anyone notifies you in writing that you have not
35 | complied with [Notices](#notices), you can keep your
36 | license by taking all practical steps to comply within 30
37 | days after the notice. If you do not do so, your license
38 | ends immediately.
39 |
40 | ## Patent
41 |
42 | Each contributor licenses you to do everything with this
43 | software that would otherwise infringe any patent claims
44 | they can license or become able to license.
45 |
46 | ## Reliability
47 |
48 | No contributor can revoke this license.
49 |
50 | ## No Liability
51 |
52 | ***As far as the law allows, this software comes as is,
53 | without any warranty or condition, and no contributor
54 | will be liable to anyone for any damages related to this
55 | software or this license, under any kind of legal claim.***
56 |
--------------------------------------------------------------------------------
/packages/docs/src/routes/+layout.server.ts:
--------------------------------------------------------------------------------
1 | import type { LayoutServerLoad } from './$types';
2 | import { parser } from '$lib/server/model';
3 | import type { Node } from 'ts-morph';
4 |
5 | export const prerender = true;
6 |
7 | const parseExports = parser
8 | .getModuleExports('@greendoc/parse')
9 | .map(createExport)
10 | .sort((a: Export, b: Export) => (a.text > b.text ? 1 : -1));
11 |
12 | interface Export {
13 | text: string;
14 | href: string;
15 | kind: string;
16 | category?: string;
17 | external?: boolean;
18 | tags?: Record;
19 | }
20 |
21 | interface Section {
22 | title: string;
23 | items?: Export[];
24 | subsections?: Subsection[];
25 | }
26 |
27 | interface Subsection {
28 | title: string;
29 | items?: Export[];
30 | }
31 |
32 | function createExport(item: Node): Export {
33 | return {
34 | text: parser.getName(item),
35 | href: parser.getPath(item)!,
36 | kind: item.getKindName(),
37 | category: parser.getTag(item, 'category') || undefined,
38 | tags: parser.getTags(item) || undefined
39 | };
40 | }
41 |
42 | export const load: LayoutServerLoad = () => {
43 | return {
44 | metadata: {
45 | title: 'greendoc',
46 | snippet: ''
47 | },
48 | navigation: {
49 | sections: [
50 | {
51 | title: 'Getting Started',
52 | items: [
53 | { text: 'Home', href: '/' },
54 | { text: 'Installation', href: '/installation' },
55 | {
56 | text: 'GitHub',
57 | external: true,
58 | href: 'https://github.com/donmccurdy/greendoc'
59 | },
60 | {
61 | text: 'NPM',
62 | external: true,
63 | href: 'https://www.npmjs.com/search?q=%40greendoc'
64 | }
65 | ],
66 | subsections: []
67 | },
68 | {
69 | title: '@greendoc/parse',
70 | items: parseExports
71 | }
72 | ] as Section[]
73 | }
74 | };
75 | };
76 |
--------------------------------------------------------------------------------
/packages/svelte/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@greendoc/svelte",
3 | "version": "0.4.1",
4 | "repository": "https://github.com/donmccurdy/greendoc",
5 | "author": "Don McCurdy ",
6 | "license": "BlueOak-1.0.0",
7 | "type": "module",
8 | "sideEffects": false,
9 | "exports": {
10 | ".": {
11 | "types": "./dist/index.d.ts",
12 | "svelte": "./dist/index.js"
13 | }
14 | },
15 | "typesVersions": {
16 | ">4.0": {
17 | "Class.svelte": [
18 | "./dist/Class.d.ts"
19 | ],
20 | "Constructor.svelte": [
21 | "./dist/Constructor.d.ts"
22 | ],
23 | "Comment.svelte": [
24 | "./dist/Comment.d.ts"
25 | ],
26 | "Enum.svelte": [
27 | "./dist/Enum.d.ts"
28 | ],
29 | "EnumMember.svelte": [
30 | "./dist/EnumMember.d.ts"
31 | ],
32 | "Function.svelte": [
33 | "./dist/Function.d.ts"
34 | ],
35 | "Interface.svelte": [
36 | "./dist/Interface.d.ts"
37 | ],
38 | "Method.svelte": [
39 | "./dist/Method.d.ts"
40 | ],
41 | "Property.svelte": [
42 | "./dist/Property.d.ts"
43 | ],
44 | "Reference.svelte": [
45 | "./dist/Reference.d.ts"
46 | ],
47 | "Sources.svelte": [
48 | "./dist/Sources.d.ts"
49 | ],
50 | "index": [
51 | "./dist/index.d.ts"
52 | ]
53 | }
54 | },
55 | "scripts": {
56 | "dev:disabled": "vite dev --force",
57 | "build": "svelte-kit sync && svelte-package -o dist",
58 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
59 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
60 | "lint": "prettier --check src",
61 | "format": "prettier --write src",
62 | "test": "echo \"Tests not yet implemented\"",
63 | "preversion": "yarn build && yarn test",
64 | "version": "rimraf dist/* && yarn build && git add -u",
65 | "postversion": "git push && git push --tags && npm publish --access public",
66 | "clean": "rimraf dist/*"
67 | },
68 | "dependencies": {
69 | "@greendoc/parse": "^0.4.1"
70 | },
71 | "peerDependencies": {
72 | "svelte": "^4.0.0"
73 | },
74 | "files": [
75 | "dist"
76 | ],
77 | "gitHead": "276870f41d0e92060bb04b138d1ee8fbe643d1d3"
78 | }
79 |
--------------------------------------------------------------------------------
/packages/svelte/src/lib/Class.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
20 |
21 | {#if data.extendsTypes.length}
22 |
23 | Hierarchy
24 |
25 | {#each data.extendsTypes as extendsType}
26 | -
27 |
28 |
29 | {/each}
30 | -
31 |
32 |
33 |
34 |
35 | {/if}
36 |
37 | {#if data.constructor}
38 |
39 | Constructor
40 |
41 |
42 | {/if}
43 |
44 | {#if data.staticProperties.length}
45 |
46 | Static properties
47 | {#each data.staticProperties as property}
48 | {#if !property.isProtected}
49 |
50 | {/if}
51 | {/each}
52 |
53 | {/if}
54 |
55 | {#if data.staticMethods.length}
56 |
57 | Static methods
58 | {#each data.staticMethods as method}
59 | {#if !method.isProtected}
60 |
61 | {/if}
62 | {/each}
63 |
64 | {/if}
65 |
66 | {#if data.properties.length}
67 |
68 | Properties
69 | {#each data.properties as property}
70 | {#if !property.isProtected}
71 |
72 | {/if}
73 | {/each}
74 |
75 | {/if}
76 |
77 | {#if data.methods.length}
78 |
79 | Methods
80 | {#each data.methods as method}
81 | {#if !method.isProtected}
82 |
83 | {/if}
84 | {/each}
85 |
86 | {/if}
87 |
88 |
89 |
--------------------------------------------------------------------------------
/packages/docs/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 | {$page.data.metadata.title}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
68 |
114 |
115 |
120 |
--------------------------------------------------------------------------------
/packages/parse/src/types.ts:
--------------------------------------------------------------------------------
1 | // TODO(design): Clean up API from an end-user perspective.
2 | // TODO(design): Consider reading through the ECMAScript type annotations proposal,
3 | // and fitting this design to its goals. If a feature is supported in TypeScript but
4 | // not in JavaScript, I might consider omitting it here.
5 | // TODO(design): Consider removing 'Api' prefix.
6 | export namespace GD {
7 | // export enum ApiExportKind {
8 | // CLASS = 'Class',
9 | // INTERFACE = 'Interface',
10 | // ENUM = 'Enum',
11 | // FUNCTION = 'Function',
12 | // VARIABLE = 'Variable'
13 | // }
14 |
15 | export enum ApiItemKind {
16 | CLASS = 'Class',
17 | INTERFACE = 'Interface',
18 | ENUM = 'Enum',
19 | ENUM_MEMBER = 'EnumMember',
20 | FUNCTION = 'Function',
21 | VARIABLE = 'Variable',
22 | CONSTRUCTOR = 'Constructor',
23 | METHOD = 'Method',
24 | METHOD_SIGNATURE = 'MethodSignature',
25 | PROPERTY = 'Property',
26 | PROPERTY_SIGNATURE = 'PropertySignature'
27 | }
28 |
29 | export type ApiItem =
30 | | ApiClass
31 | | ApiInterface
32 | | ApiFunction
33 | | ApiMember
34 | | ApiMethod
35 | | ApiEnum
36 | | ApiEnumMember
37 | | ApiVariable;
38 |
39 | export interface ApiItemBase {
40 | name: string;
41 | kind: ApiItemKind;
42 | source?: Source;
43 | tags?: Record;
44 | }
45 |
46 | export interface ApiClass extends ApiItemBase {
47 | // TODO: resolved & unresolved generics?
48 | // TODO: interfaces implemented?
49 | kind: ApiItemKind.CLASS;
50 | comment?: string; // → IntlText
51 | extendsTypes: Reference[];
52 | constructor?: ApiConstructor;
53 | properties: ApiProperty[];
54 | methods: ApiMethod[];
55 | // TODO: overloads?
56 | staticProperties: ApiProperty[];
57 | staticMethods: ApiMethod[];
58 | }
59 |
60 | export interface ApiInterface extends ApiItemBase {
61 | kind: ApiItemKind.INTERFACE;
62 | comment?: string;
63 | extendsTypes: Reference[];
64 | properties: ApiProperty[];
65 | methods: ApiMethod[];
66 | }
67 |
68 | export interface ApiFunction extends ApiItemBase {
69 | kind: ApiItemKind.FUNCTION;
70 | comment?: string;
71 | params: ApiParameter[];
72 | returns: ApiReturnType;
73 | returnsComment?: string;
74 | }
75 |
76 | export interface ApiMember extends ApiItemBase {
77 | kind: ApiItemKind.CONSTRUCTOR | ApiItemKind.METHOD | ApiItemKind.PROPERTY;
78 | isStatic?: boolean;
79 | isProtected?: boolean;
80 | isOptional?: boolean;
81 | overwrite?: Reference;
82 | comment?: string;
83 | }
84 |
85 | export interface ApiMethod extends ApiMember {
86 | kind: ApiItemKind.METHOD;
87 | params: ApiParameter[];
88 | returns: ApiReturnType;
89 | returnsComment?: string;
90 | }
91 |
92 | export type ApiParameter = {
93 | name: string;
94 | type?: Token;
95 | optional?: boolean;
96 | };
97 |
98 | export type ApiReturnType = Token;
99 |
100 | export interface ApiConstructor extends ApiMember {
101 | kind: ApiItemKind.CONSTRUCTOR;
102 | isStatic: false;
103 | name: 'constructor';
104 | params: ApiParameter[];
105 | returns: ApiReturnType;
106 | }
107 |
108 | export interface ApiProperty extends ApiMember {
109 | kind: ApiItemKind.PROPERTY;
110 | type?: Token;
111 | isReadonly: boolean;
112 | }
113 |
114 | export interface ApiEnum extends ApiItemBase {
115 | kind: ApiItemKind.ENUM;
116 | members: ApiEnumMember[];
117 | comment?: string;
118 | }
119 |
120 | export interface ApiEnumMember extends ApiItemBase {
121 | kind: ApiItemKind.ENUM_MEMBER;
122 | type?: Token;
123 | comment?: string;
124 | }
125 |
126 | export interface ApiVariable extends ApiItemBase {
127 | type?: Token;
128 | comment?: string; // not yet supported by ts-morph
129 | }
130 |
131 | export type Token = string | Reference;
132 |
133 | export interface Reference {
134 | name: string;
135 | kind: ApiItemKind;
136 | path?: string;
137 | }
138 |
139 | export interface Source {
140 | text: string;
141 | url: string;
142 | }
143 |
144 | export interface ApiTypeAlias extends ApiItemBase {}
145 | }
146 |
--------------------------------------------------------------------------------
/packages/parse/src/Parser.ts:
--------------------------------------------------------------------------------
1 | import * as TS from 'ts-morph';
2 | import { markedFormatter } from './utils/format';
3 | import { SUPPORTED_TAGS } from './constants';
4 |
5 | type $StringLike = { toString: () => string };
6 |
7 | interface Module {
8 | name: string;
9 | slug: string;
10 | rootDirectory: string;
11 | entry: TS.SourceFile;
12 | }
13 |
14 | export interface ModuleConfig {
15 | name: string;
16 | slug: string;
17 | entry: TS.SourceFile | string;
18 | }
19 |
20 | export class Parser {
21 | readonly project: TS.Project;
22 | readonly modules: Module[] = [];
23 | readonly itemToSlug = new Map();
24 | readonly slugToItem = new Map();
25 | readonly exportToItem = new Map();
26 | // TODO(design): Clarify if/that this is a URL path, not a path on disk.
27 | private rootPath: string = '';
28 | private baseURL: string = '';
29 | private formatter: (md: string) => string = markedFormatter;
30 |
31 | constructor(project = new TS.Project()) {
32 | this.project = project;
33 | }
34 |
35 | public init(): this {
36 | return this;
37 | }
38 |
39 | public setRootPath(path: string) {
40 | this.rootPath = path;
41 | return this;
42 | }
43 |
44 | public setBaseURL(url: string) {
45 | this.baseURL = url;
46 | return this;
47 | }
48 |
49 | public setMarkdownRenderer(formatter: ((md: string) => string) | null): this {
50 | this.formatter = formatter || markedFormatter;
51 | return this;
52 | }
53 |
54 | public addModule(config: ModuleConfig): this {
55 | let entry: TS.SourceFile;
56 | if (config.entry instanceof TS.SourceFile) {
57 | entry = config.entry;
58 | } else {
59 | entry = this.project.addSourceFileAtPath(config.entry);
60 | }
61 |
62 | const module: Module = {
63 | name: config.name,
64 | slug: config.slug,
65 | rootDirectory: fs.dirname(entry.getFilePath()),
66 | entry: entry
67 | };
68 | this.modules.push(module);
69 |
70 | for (const [name, items] of module.entry.getExportedDeclarations()) {
71 | for (const item of items) {
72 | if (this.isHidden(item)) continue;
73 |
74 | const slug = name;
75 | this.itemToSlug.set(item, slug);
76 | this.slugToItem.set(slug, item);
77 | this.exportToItem.set(name, item);
78 | }
79 | }
80 | return this;
81 | }
82 |
83 | public getModuleExports(name: string): TS.Node[] {
84 | const module = this.modules.find((module) => module.name === name);
85 | const exports = [];
86 | for (const [name, items] of module.entry.getExportedDeclarations()) {
87 | for (const item of items) {
88 | if (this.isHidden(item)) continue;
89 | exports.push(item);
90 | }
91 | }
92 | return exports;
93 | }
94 |
95 | /** @internal */
96 | getItemBySlug(slug: string): TS.Node {
97 | const item = this.slugToItem.get(slug);
98 | if (item) return item;
99 | throw new Error(`Item for "${slug}" not found`);
100 | }
101 |
102 | /** @internal */
103 | getItemByExportName(name: string): TS.Node {
104 | const item = this.exportToItem.get(name);
105 | if (item) return item;
106 | throw new Error(`Item for "${name}" not found`);
107 | }
108 |
109 | /** @internal */
110 | hasItem(item: TS.Node): boolean {
111 | return this.itemToSlug.has(item);
112 | }
113 |
114 | /** @internal */
115 | getSlug(item: TS.Node): string {
116 | const slug = this.itemToSlug.get(item);
117 | if (slug) return slug;
118 |
119 | throw new Error(
120 | `Slug for "${item.getKindName()}" from "${item.getSourceFile().getBaseName()}" not found`
121 | );
122 | }
123 |
124 | // TODO(design): URL paths should be an application-level decision.
125 | /** @internal */
126 | getPath(item: TS.Node): string | null {
127 | const module = this.getModule(item);
128 | if (!module) return null;
129 |
130 | if (this.isHidden(item)) return null;
131 |
132 | switch (item.getKind()) {
133 | case TS.SyntaxKind.ClassDeclaration:
134 | return `/modules/${module.slug}/classes/${this.getSlug(item)}`;
135 | case TS.SyntaxKind.InterfaceDeclaration:
136 | return `/modules/${module.slug}/interfaces/${this.getSlug(item)}`;
137 | case TS.SyntaxKind.EnumDeclaration:
138 | return `/modules/${module.slug}/enums/${this.getSlug(item)}`;
139 | case TS.SyntaxKind.FunctionDeclaration:
140 | return `/modules/${module.slug}/functions/${this.getSlug(item)}`;
141 | // case TS.SyntaxKind.VariableDeclaration:
142 | // return `/modules/${module.slug}/constants/${this.getSlug(item)}`;
143 | default:
144 | return null;
145 | }
146 | }
147 |
148 | getModule(item: TS.Node): Module | null {
149 | const file = item.getSourceFile();
150 | if (file.isFromExternalLibrary()) return null;
151 | if (file.isDeclarationFile()) return null;
152 |
153 | const filePath = file.getFilePath();
154 | for (const module of this.modules) {
155 | if (filePath.startsWith(module.rootDirectory)) {
156 | return module;
157 | }
158 | }
159 |
160 | throw new Error(`Module not found for path "${filePath}".`);
161 | }
162 |
163 | getTags(item: TS.Node): Record | null {
164 | const tags: Record = {};
165 | let tagCount = 0;
166 | if ((item as unknown as TS.JSDocableNode).getJsDocs) {
167 | for (const doc of (item as unknown as TS.JSDocableNode).getJsDocs()) {
168 | for (const tag of doc.getTags()) {
169 | const tagName = tag.getTagName();
170 | if (SUPPORTED_TAGS.has(tagName)) {
171 | tags[tagName] = tag.getCommentText() || true;
172 | tagCount++;
173 | }
174 | }
175 | }
176 | }
177 | return tagCount > 0 ? tags : null;
178 | }
179 |
180 | getTag(item: TS.Node, tagName: string): string | null {
181 | if ((item as unknown as TS.JSDocableNode).getJsDocs) {
182 | for (const doc of (item as unknown as TS.JSDocableNode).getJsDocs()) {
183 | for (const tag of doc.getTags()) {
184 | if (tag.getTagName() === tagName) {
185 | return tag.getCommentText();
186 | }
187 | }
188 | }
189 | }
190 | return null;
191 | }
192 |
193 | getName(item: TS.Node): string {
194 | if ((item as any).getName) return (item as any).getName();
195 | return '';
196 | }
197 |
198 | getSourceText(item: TS.Node): string {
199 | const file = item.getSourceFile();
200 | if (file.isFromExternalLibrary()) return 'external';
201 | if (file.isDeclarationFile()) return 'external';
202 | const url = file.getFilePath() as string;
203 | if (url.startsWith(this.rootPath)) {
204 | return url.replace(this.rootPath + '/', '');
205 | }
206 | return url;
207 | }
208 |
209 | getSourceURL(item: TS.Node): string {
210 | const file = item.getSourceFile();
211 | if (file.isFromExternalLibrary()) return '';
212 | if (file.isDeclarationFile()) return '';
213 | let url = file.getFilePath() as string;
214 | if (url.startsWith(this.rootPath)) {
215 | url = this.baseURL + url.replace(this.rootPath, '');
216 | }
217 | return url;
218 | }
219 |
220 | isHidden(item: TS.Node): boolean {
221 | if ((item as unknown as TS.JSDocableNode).getJsDocs) {
222 | for (const doc of (item as unknown as TS.JSDocableNode).getJsDocs()) {
223 | for (const tag of doc.getTags()) {
224 | if (tag.getTagName() === 'hidden') return true;
225 | if (tag.getTagName() === 'internal') return true;
226 | }
227 | }
228 | }
229 | return false;
230 | }
231 |
232 | renderMarkdown(md: string): string {
233 | return this.formatter(md);
234 | }
235 | }
236 |
237 | const fs = {
238 | basename(uri: string): string {
239 | const fileName = uri.split(/[\\/]/).pop()!;
240 | return fileName.substring(0, fileName.lastIndexOf('.'));
241 | },
242 | dirname(uri: string): string {
243 | return uri.match(/(.*)[\/\\]/)[1] || '';
244 | }
245 | };
246 |
--------------------------------------------------------------------------------
/packages/parse/test/index.test.ts:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 | import { Encoder, GD, Parser, createDefaultSort, createPrefixSort } from '@greendoc/parse';
3 | import * as TS from 'ts-morph';
4 |
5 | const MOCK_ROOT_PATH = '/path/to';
6 | const MOCK_SOURCE_PATH = '/path/to/source.ts';
7 | const MOCK_BASE_URL = 'https://example.com/api';
8 |
9 | test('parser.modules', (t) => {
10 | const parser = createParser(
11 | 'my-package',
12 | `
13 | export class Animal {}
14 | export class Dog {}
15 | export class Snake {}
16 | export class Freddy {}
17 | `
18 | );
19 | const pkg = parser.modules[0];
20 | t.is(pkg.name, 'my-package', 'package name');
21 | t.deepEqual(
22 | parser.getModuleExports(pkg.name).map((item) => (item as any).getName()),
23 | ['Animal', 'Dog', 'Snake', 'Freddy'],
24 | 'package exports'
25 | );
26 | });
27 |
28 | test('parser.modules - tags', (t) => {
29 | const parser = createParser(
30 | 'my-package',
31 | `
32 | /**
33 | * Description.
34 | * @type {string} name
35 | * @alpha
36 | * @beta
37 | * @deprecated Use something else.
38 | * @experimental
39 | */
40 | export function myFunction() {}
41 |
42 | /**
43 | * @experimental
44 | */
45 | export class MyClass {}
46 | `
47 | );
48 |
49 | const exports = parser.getModuleExports('my-package');
50 |
51 | t.deepEqual(
52 | exports.map((item) => [parser.getName(item), parser.getTags(item)]),
53 | [
54 | [
55 | 'myFunction',
56 | {
57 | alpha: true,
58 | beta: true,
59 | deprecated: 'Use something else.',
60 | experimental: true
61 | }
62 | ],
63 | ['MyClass', { experimental: true }]
64 | ],
65 | 'tags'
66 | );
67 | });
68 |
69 | test('class serialization', (t) => {
70 | const parser = createParser(
71 | 'my-package',
72 | `
73 | /** Description of Animal. */
74 | export class Animal {
75 | private name: string;
76 | /** Description of Animal#getName. */
77 | getName(): string {
78 | return this.name;
79 | }
80 | }
81 |
82 | /** Description of Dog. */
83 | export class Dog extends Animal {
84 | /** Description of Dog#getLegs. */
85 | getLegs(): number {
86 | return 4;
87 | }
88 | }
89 |
90 | /** Description of Cat. */
91 | export class Snake extends Animal {
92 | /** Description of Snake#getFangs. */
93 | getFangs(): number {
94 | return 2;
95 | }
96 | }
97 |
98 | export class Freddy extends Dog {
99 | getName(): string {
100 | return 'Freddy';
101 | }
102 | }
103 | `
104 | );
105 |
106 | const encoder = new Encoder(parser);
107 | const dog = parser.getItemBySlug('Dog') as TS.ClassDeclaration;
108 | const encodedDog = trim(encoder.encodeItem(dog));
109 | t.deepEqual(
110 | encodedDog,
111 | {
112 | kind: 'Class',
113 | name: 'Dog',
114 | source: {
115 | text: 'source.ts',
116 | url: 'https://example.com/api/source.ts'
117 | },
118 | comment: 'Description of Dog.
\n',
119 | extendsTypes: [
120 | {
121 | path: '/modules/my-package/classes/Animal',
122 | name: 'Animal',
123 | kind: 'Class'
124 | }
125 | ],
126 | staticProperties: [],
127 | properties: [],
128 | staticMethods: [],
129 | methods: [
130 | {
131 | comment: 'Description of Dog#getLegs.
\n',
132 | kind: GD.ApiItemKind.METHOD,
133 | name: 'getLegs',
134 | params: [],
135 | returns: 'number',
136 | source: {
137 | text: 'source.ts',
138 | url: 'https://example.com/api/source.ts'
139 | }
140 | },
141 | {
142 | comment: 'Description of Animal#getName.
\n',
143 | kind: GD.ApiItemKind.METHOD,
144 | name: 'getName',
145 | params: [],
146 | returns: 'string',
147 | source: {
148 | text: 'source.ts',
149 | url: 'https://example.com/api/source.ts'
150 | }
151 | }
152 | ]
153 | },
154 | 'encoded class'
155 | );
156 | });
157 |
158 | test('constructors', (t) => {
159 | const parser = createParser(
160 | 'my-package',
161 | `
162 | export class A {
163 | constructor(a: string, b: string) {
164 | // ...
165 | }
166 | }
167 | `
168 | );
169 |
170 | const encoder = new Encoder(parser);
171 | const classA = parser.getItemBySlug('A') as TS.ClassDeclaration;
172 | const encodedClassA = trim(encoder.encodeItem(classA));
173 |
174 | t.deepEqual(
175 | encodedClassA.constructor,
176 | {
177 | kind: GD.ApiItemKind.CONSTRUCTOR,
178 | name: 'constructor',
179 | params: [
180 | { name: 'a', type: 'string' },
181 | { name: 'b', type: 'string' }
182 | ],
183 | returns: { kind: 'Class', name: 'A', path: '/modules/my-package/classes/A' },
184 | source: { text: 'source.ts', url: 'https://example.com/api/source.ts' }
185 | },
186 | 'a.constructor'
187 | );
188 | });
189 |
190 | test('inherited members', (t) => {
191 | const parser = createParser(
192 | 'my-package',
193 | `
194 | export class A {
195 | static parentStaticProperty = 1;
196 | parentProperty = 2;
197 | static parentStaticMethod (): number { return 3; }
198 | parentMethod (): number { return 4; }
199 | }
200 |
201 | export class B extends A {
202 | static childStaticProperty = 5;
203 | childProperty = 6;
204 | static childStaticMethod (): number { return 7; }
205 | childMethod (): number { return 8; }
206 | }
207 | `
208 | );
209 |
210 | const encoder = new Encoder(parser);
211 | const a = encoder.encodeItem(parser.getItemBySlug('A') as TS.ClassDeclaration);
212 | const b = encoder.encodeItem(parser.getItemBySlug('B') as TS.ClassDeclaration);
213 |
214 | // methods
215 | t.deepEqual(a.methods.map(toName), ['parentMethod'], 'a.methods');
216 | t.deepEqual(b.methods.map(toName), ['childMethod', 'parentMethod'], 'b.methods');
217 |
218 | // static methods
219 | t.deepEqual(a.staticMethods.map(toName), ['parentStaticMethod'], 'a.staticMethods');
220 | t.deepEqual(
221 | b.staticMethods.map(toName),
222 | ['childStaticMethod', 'parentStaticMethod'],
223 | 'b.staticMethods'
224 | );
225 |
226 | // properties
227 | t.deepEqual(a.properties.map(toName), ['parentProperty'], 'a.properties');
228 | t.deepEqual(b.properties.map(toName), ['childProperty', 'parentProperty'], 'b.properties');
229 |
230 | // static properties
231 | t.deepEqual(a.staticProperties.map(toName), ['parentStaticProperty'], 'a.staticProperties');
232 | t.deepEqual(
233 | b.staticProperties.map(toName),
234 | ['childStaticProperty', 'parentStaticProperty'],
235 | 'b.staticProperties'
236 | );
237 | });
238 |
239 | test('custom sort', (t) => {
240 | const parser = createParser(
241 | 'my-package',
242 | `
243 | export class A {
244 | a() {}
245 | getA() {}
246 | setA() {}
247 | listA() {}
248 | b() {}
249 | getB() {}
250 | setB() {}
251 | listB() {}
252 | }
253 | `
254 | );
255 |
256 | const encoder = new Encoder(parser);
257 | const a = parser.getItemBySlug('A') as TS.ClassDeclaration;
258 |
259 | let encodedA = encoder.encodeItem(a);
260 | t.deepEqual(
261 | encodedA.methods.map(toName),
262 | ['a', 'b', 'getA', 'getB', 'listA', 'listB', 'setA', 'setB'],
263 | 'default sort'
264 | );
265 |
266 | encodedA = encoder.setSort(createPrefixSort()).encodeItem(a);
267 | t.deepEqual(
268 | encodedA.methods.map(toName),
269 | ['a', 'getA', 'listA', 'setA', 'b', 'getB', 'listB', 'setB'],
270 | 'prefix sort'
271 | );
272 |
273 | encodedA = encoder.setSort(createDefaultSort()).encodeItem(a);
274 | t.deepEqual(
275 | encodedA.methods.map(toName),
276 | ['a', 'b', 'getA', 'getB', 'listA', 'listB', 'setA', 'setB'],
277 | 'default sort (reset)'
278 | );
279 | });
280 |
281 | test('tsdoc comments, params, and returns', (t) => {
282 | const parser = createParser(
283 | 'my-package',
284 | `
285 | /**
286 | * Concatenates two string arguments.
287 | * @param a First argument.
288 | * @param b Second argument.
289 | * @returns Concatenation of first and second arguments.
290 | */
291 | export function concat(a: string, b: string): string {
292 | return a + b;
293 | }
294 |
295 | /** Class that adds numbers. */
296 | export class AddingMachine {
297 | /**
298 | * Sums two numbers.
299 | * @param a First addend.
300 | * @param b Second addend.
301 | * @returns Sum of first and second addends.
302 | */
303 | add(a: number, b: number): number {
304 | return a + b;
305 | }
306 | }
307 | `
308 | );
309 |
310 | const encoder = new Encoder(parser);
311 | const concat = parser.getItemBySlug('concat') as TS.FunctionDeclaration;
312 | const encodedConcat = trim(encoder.encodeItem(concat));
313 |
314 | t.deepEqual(
315 | encodedConcat,
316 | {
317 | kind: 'Function',
318 | name: 'concat',
319 | source: { text: 'source.ts', url: 'https://example.com/api/source.ts' },
320 | params: [
321 | { name: 'a', type: 'string', comment: 'First argument.
\n' },
322 | { name: 'b', type: 'string', comment: 'Second argument.
\n' }
323 | ],
324 | returns: 'string',
325 | returnsComment: 'Concatenation of first and second arguments.
\n',
326 | comment: 'Concatenates two string arguments.
\n'
327 | },
328 | 'function'
329 | );
330 |
331 | const addingMachine = parser.getItemBySlug('AddingMachine') as TS.ClassDeclaration;
332 | const encodedAddingMachine = trim(encoder.encodeItem(addingMachine));
333 |
334 | t.deepEqual(
335 | encodedAddingMachine,
336 | {
337 | kind: 'Class',
338 | name: 'AddingMachine',
339 | comment: 'Class that adds numbers.
\n',
340 | source: { text: 'source.ts', url: 'https://example.com/api/source.ts' },
341 | methods: [
342 | {
343 | kind: 'Method',
344 | name: 'add',
345 | source: { text: 'source.ts', url: 'https://example.com/api/source.ts' },
346 | comment: 'Sums two numbers.
\n',
347 | params: [
348 | { name: 'a', type: 'number', comment: 'First addend.
\n' },
349 | { name: 'b', type: 'number', comment: 'Second addend.
\n' }
350 | ],
351 | returns: 'number',
352 | returnsComment: 'Sum of first and second addends.
\n'
353 | }
354 | ],
355 | extendsTypes: [],
356 | staticProperties: [],
357 | properties: [],
358 | staticMethods: []
359 | },
360 | 'class'
361 | );
362 | });
363 |
364 | test('item tags', (t) => {
365 | const parser = createParser(
366 | 'my-package',
367 | `
368 | /**
369 | * My class.
370 | * @public
371 | */
372 | export class AddingMachine {
373 | /** @deprecated Do not use this. */
374 | public myProperty = 25;
375 | /**
376 | * Sums two numbers.
377 | * @param a First addend.
378 | * @param b Second addend.
379 | * @returns Sum of first and second addends.
380 | * @public
381 | */
382 | add(a: number, b: number): number {
383 | return a + b;
384 | }
385 | /** @deprecated */
386 | subtract(a: number, b: number): number {
387 | return a - b;
388 | }
389 | /** @alpha */
390 | multiply(a: number, b: number): number {
391 | return a * b;
392 | }
393 | }
394 | `
395 | );
396 |
397 | const encoder = new Encoder(parser);
398 | const addingMachine = parser.getItemBySlug('AddingMachine') as TS.ClassDeclaration;
399 | const encodedAddingMachine = trim(encoder.encodeItem(addingMachine));
400 | const addMethod = encodedAddingMachine.methods.find(({ name }) => name === 'add');
401 | const subtractMethod = encodedAddingMachine.methods.find(({ name }) => name === 'subtract');
402 | const multiplyMethod = encodedAddingMachine.methods.find(({ name }) => name === 'multiply');
403 |
404 | t.deepEqual(encodedAddingMachine.tags, { public: true }, 'class.tags');
405 | t.deepEqual(
406 | encodedAddingMachine.properties[0].tags,
407 | { deprecated: 'Do not use this.' },
408 | 'class.property.tags'
409 | );
410 | t.deepEqual(addMethod.tags, { public: true }, 'class#add.tags');
411 | t.deepEqual(subtractMethod.tags, { deprecated: true }, 'class#subtract.tags');
412 | t.deepEqual(multiplyMethod.tags, { alpha: true }, 'class#multiply.tags');
413 | });
414 |
415 | test('variables', (t) => {
416 | const parser = createParser(
417 | 'my-package',
418 | `
419 | /**
420 | * Description.
421 | * @type {string} name
422 | */
423 | export let name = 'hello';
424 | `
425 | );
426 |
427 | const encoder = new Encoder(parser);
428 | const encodedVariable = trim(
429 | encoder.encodeItem(parser.getItemBySlug('name') as TS.VariableDeclaration)
430 | );
431 |
432 | t.deepEqual(
433 | encodedVariable,
434 | {
435 | kind: 'Variable',
436 | name: 'name',
437 | source: { text: 'source.ts', url: 'https://example.com/api/source.ts' },
438 | type: 'string'
439 | // comment: 'Description.
\n' // not yet supported by ts-morph
440 | },
441 | 'variable'
442 | );
443 | });
444 |
445 | //////////////////////// UTILITIES ////////////////////////
446 |
447 | /** Trims properties that JSON serialization would exclude. */
448 | function trim(object: T): T {
449 | return JSON.parse(JSON.stringify(object));
450 | }
451 |
452 | function toName(object: { name: string }): string {
453 | return object.name;
454 | }
455 |
456 | function createParser(name: string, source: string) {
457 | const project = new TS.Project();
458 | const file = project.createSourceFile(MOCK_SOURCE_PATH, source);
459 | return new Parser(project)
460 | .setRootPath(MOCK_ROOT_PATH)
461 | .setBaseURL(MOCK_BASE_URL)
462 | .addModule({ name: name, slug: name, entry: file })
463 | .init();
464 | }
465 |
--------------------------------------------------------------------------------
/packages/parse/src/Encoder.ts:
--------------------------------------------------------------------------------
1 | import { GD } from './types';
2 | import { Parser } from './Parser';
3 | import * as TS from 'ts-morph';
4 | import { SortFn, createDefaultSort } from './utils/sort';
5 |
6 | /**
7 | * Encodes a serialized representation of an exported item in the API, such as
8 | * a class, enum, or function.
9 | */
10 | export class Encoder {
11 | private _sort = createDefaultSort();
12 | private readonly _parser: Parser;
13 |
14 | /**
15 | * Creates a reusable Encoder instance, bound to the given {@link Parser}.
16 | */
17 | constructor(parser: Parser) {
18 | this._parser = parser;
19 | }
20 |
21 | encodeItem(item: TS.EnumDeclaration): GD.ApiEnum;
22 | encodeItem(item: TS.InterfaceDeclaration): GD.ApiInterface;
23 | encodeItem(item: TS.ClassDeclaration): GD.ApiClass;
24 | encodeItem(item: TS.EnumDeclaration): GD.ApiEnum;
25 | encodeItem(item: TS.EnumMember): GD.ApiEnumMember;
26 | encodeItem(item: TS.TypeAliasDeclaration): GD.ApiTypeAlias;
27 | encodeItem(item: TS.FunctionDeclaration): GD.ApiFunction;
28 | encodeItem(item: TS.Node): GD.ApiItem;
29 | encodeItem(item: TS.Node): GD.ApiItem {
30 | switch (item.getKind()) {
31 | case TS.SyntaxKind.ClassDeclaration:
32 | return this._encodeClass(item as TS.ClassDeclaration);
33 | case TS.SyntaxKind.InterfaceDeclaration:
34 | return this._encodeInterface(item as TS.InterfaceDeclaration);
35 | case TS.SyntaxKind.EnumDeclaration:
36 | return this._encodeEnum(item as TS.EnumDeclaration);
37 | case TS.SyntaxKind.FunctionDeclaration:
38 | return this._encodeFunction(item as TS.FunctionDeclaration);
39 | case TS.SyntaxKind.EnumMember:
40 | return this._encodeEnumMember(item as TS.EnumMember);
41 | case TS.SyntaxKind.TypeAliasDeclaration:
42 | return this._encodeTypeAlias(item as TS.TypeAliasDeclaration) as any; // TODO(cleanup)
43 | case TS.SyntaxKind.PropertyDeclaration:
44 | case TS.SyntaxKind.MethodDeclaration:
45 | throw new Error(`Unexpected detached type, "${item.getKindName()}"`);
46 | case TS.SyntaxKind.VariableDeclaration:
47 | return this._encodeVariable(item as TS.VariableDeclaration);
48 | default:
49 | console.log(item);
50 | throw new Error(`Unsupported encoded type, "${item.getKindName()}"`);
51 | }
52 | }
53 |
54 | setSort(sort: SortFn): this {
55 | this._sort = sort;
56 | return this;
57 | }
58 |
59 | protected _encodeItem(item: TS.Node): GD.ApiItem {
60 | return {
61 | kind: this._encodeKind(item.getKind()) as any, // TODO(cleanup)
62 | name: (item as any).getName ? (item as any).getName() : '',
63 | source: {
64 | text: this._parser.getSourceText(item),
65 | url: this._parser.getSourceURL(item)
66 | },
67 | tags: this._parser.getTags(item) || undefined
68 | };
69 | }
70 |
71 | protected _encodeKind(kind: TS.SyntaxKind): GD.ApiItemKind {
72 | switch (kind) {
73 | case TS.SyntaxKind.ClassDeclaration:
74 | return GD.ApiItemKind.CLASS;
75 | case TS.SyntaxKind.InterfaceDeclaration:
76 | return GD.ApiItemKind.INTERFACE;
77 | case TS.SyntaxKind.EnumDeclaration:
78 | return GD.ApiItemKind.ENUM;
79 | case TS.SyntaxKind.EnumMember:
80 | return GD.ApiItemKind.ENUM_MEMBER;
81 | case TS.SyntaxKind.FunctionDeclaration:
82 | return GD.ApiItemKind.FUNCTION;
83 | case TS.SyntaxKind.Constructor:
84 | case TS.SyntaxKind.ConstructSignature:
85 | return GD.ApiItemKind.CONSTRUCTOR;
86 | case TS.SyntaxKind.MethodDeclaration:
87 | return GD.ApiItemKind.METHOD;
88 | case TS.SyntaxKind.MethodSignature:
89 | return GD.ApiItemKind.METHOD_SIGNATURE;
90 | case TS.SyntaxKind.PropertyDeclaration:
91 | return GD.ApiItemKind.PROPERTY;
92 | case TS.SyntaxKind.PropertySignature:
93 | return GD.ApiItemKind.PROPERTY_SIGNATURE;
94 | case TS.SyntaxKind.VariableDeclaration:
95 | return GD.ApiItemKind.VARIABLE;
96 | default:
97 | throw new Error(`SyntaxKind.${getKindName(kind)} not implemented.`);
98 | }
99 | }
100 |
101 | protected _encodeClass(item: TS.ClassDeclaration): GD.ApiClass {
102 | const parser = this._parser;
103 | const constructor = getInheritedConstructor(item);
104 | const data = {
105 | ...this._encodeItem(item),
106 | comment: this._encodeComment(item.getJsDocs().pop()),
107 | extendsTypes: [],
108 | constructor: constructor ? this._encodeConstructor(constructor, item) : undefined,
109 | staticProperties: getInheritedMembers(item, TS.SyntaxKind.PropertyDeclaration, true)
110 | .filter((prop) => !parser.isHidden(prop))
111 | .map((prop) => this._encodeProperty(prop as TS.PropertyDeclaration))
112 | .sort(this._sort),
113 | properties: getInheritedMembers(item, TS.SyntaxKind.PropertyDeclaration, false)
114 | .filter((prop) => prop.getScope() !== TS.Scope.Private)
115 | .filter((prop) => !parser.isHidden(prop))
116 | .map((prop) => this._encodeProperty(prop as TS.PropertyDeclaration))
117 | .sort(this._sort),
118 | staticMethods: getInheritedMembers(item, TS.SyntaxKind.MethodDeclaration, true)
119 | .filter((method) => !parser.isHidden(method))
120 | .map((method) => this._encodeMethod(method))
121 | .sort(this._sort),
122 | methods: getInheritedMembers(item, TS.SyntaxKind.MethodDeclaration, false)
123 | .filter((method) => method.getScope() !== TS.Scope.Private)
124 | .filter((method) => !parser.isHidden(method))
125 | .map((method) => this._encodeMethod(method))
126 | .sort(this._sort)
127 | } as GD.ApiClass;
128 |
129 | let base = item;
130 | while ((base = base.getBaseClass())) {
131 | data.extendsTypes.push(this._encodeReference(base));
132 | }
133 | data.extendsTypes.reverse();
134 |
135 | return data;
136 | }
137 |
138 | protected _encodeInterface(item: TS.InterfaceDeclaration): GD.ApiInterface {
139 | return {
140 | ...this._encodeItem(item),
141 | comment: this._encodeComment(item.getJsDocs().pop()),
142 | extendsTypes: [], // item.extendsTypes.map(({ excerpt }) => this._encodeExcerpt(excerpt)),
143 | properties: item
144 | .getProperties()
145 | .filter((prop) => !this._parser.isHidden(prop))
146 | .map((prop) => this._encodeProperty(prop as any))
147 | .sort(this._sort),
148 | methods: item
149 | .getMethods()
150 | .filter((method) => !this._parser.isHidden(method))
151 | .map((method) => this._encodeMethod(method as any))
152 | .sort(this._sort)
153 | } as GD.ApiInterface;
154 | }
155 |
156 | protected _encodeEnum(item: TS.EnumDeclaration): GD.ApiEnum {
157 | const data = {
158 | ...this._encodeItem(item),
159 | members: item
160 | .getMembers()
161 | .filter((item) => !this._parser.isHidden(item))
162 | .map((item) => this._encodeEnumMember(item))
163 | .sort(this._sort)
164 | } as GD.ApiEnum;
165 |
166 | const comment = item.getJsDocs().pop();
167 | if (comment) data.comment = this._encodeComment(comment);
168 |
169 | return data;
170 | }
171 | protected _encodeFunction(item: TS.FunctionDeclaration): GD.ApiFunction {
172 | const comment = item.getJsDocs().pop();
173 | const data = {
174 | ...this._encodeItem(item),
175 | kind: GD.ApiItemKind.FUNCTION,
176 | params: item.getParameters().map((param) => ({
177 | name: param.getName(),
178 | type: this._encodeType(param.getType(), param.getTypeNode()),
179 | comment: comment ? this._encodeParamComment(comment, param.getName()) : undefined,
180 | optional: param.isOptional() || undefined
181 | })),
182 | returns: this._encodeType(item.getReturnType(), item.getReturnTypeNode()),
183 | returnsComment: comment ? this._encodeReturnsComment(comment) : undefined
184 | } as Partial;
185 |
186 | if (comment) data.comment = this._encodeComment(comment);
187 |
188 | return data as GD.ApiFunction;
189 | }
190 | protected _encodeEnumMember(item: TS.EnumMember): GD.ApiEnumMember {
191 | return {
192 | ...this._encodeItem(item),
193 | type: this._encodeType(item.getType()),
194 | comment: this._encodeComment(item.getJsDocs().pop())
195 | } as GD.ApiEnumMember;
196 | }
197 | protected _encodeTypeAlias(item: TS.TypeAliasDeclaration): GD.ApiTypeAlias {
198 | return this._encodeItem(item) as GD.ApiTypeAlias;
199 | }
200 |
201 | protected _encodeVariable(item: TS.VariableDeclaration): GD.ApiVariable {
202 | return {
203 | ...this._encodeItem(item),
204 | type: this._encodeType(item.getType())
205 | };
206 | }
207 |
208 | protected _encodeReference(item: TS.Node): GD.Reference | null {
209 | return {
210 | path: this._parser.getPath(item),
211 | name: (item as any).getName ? (item as any).getName() : '',
212 | kind: this._encodeKind(item.getKind())
213 | };
214 | }
215 |
216 | protected _encodeType(type: TS.Type, typeNode?: TS.TypeNode): GD.Token {
217 | const symbol = type.getSymbol();
218 | if (symbol) {
219 | for (const decl of symbol.getDeclarations()) {
220 | if (!this._parser.hasItem(decl)) continue;
221 | const ref = this._encodeReference(decl);
222 | if (ref) return ref;
223 | }
224 | }
225 | if (typeNode) return typeNode.getText();
226 | if (type.isAnonymous()) return 'unknown';
227 | return type.getText();
228 | }
229 |
230 | protected _encodeComment(comment?: TS.JSDoc): string {
231 | if (!comment) return '';
232 |
233 | let md = comment.getCommentText();
234 | if (!md) return '';
235 |
236 | md = md.replaceAll(/{@link ([\S]+)\s*(\S+)?\s*}/g, (_, anchorRef, anchorText) => {
237 | const [exportName, fragment] = anchorRef.split('.');
238 | const item = this._parser.getItemByExportName(exportName);
239 | const text = anchorText || anchorRef;
240 | const href = this._parser.getPath(item) + (fragment ? `#${fragment}` : '');
241 | return `[${text}](${href})`;
242 | });
243 |
244 | return this._parser.renderMarkdown(md) || '';
245 | }
246 |
247 | protected _encodeParamComment(comment: TS.JSDoc, name: string): string | undefined {
248 | for (const tag of comment.getTags()) {
249 | const tagName = tag.getTagName();
250 | if (tagName === 'param' || tagName === 'arg' || tagName === 'argument') {
251 | const paramTag = tag as TS.JSDocParameterTag;
252 | if (paramTag.getName() === name) {
253 | return this._encodeComment(paramTag as unknown as TS.JSDoc);
254 | }
255 | }
256 | }
257 | return undefined;
258 | }
259 |
260 | protected _encodeReturnsComment(comment?: TS.JSDoc): string | undefined {
261 | for (const tag of comment.getTags()) {
262 | const tagName = tag.getTagName();
263 | if (tagName === 'returns' || tagName === 'return') {
264 | return this._encodeComment(tag as unknown as TS.JSDoc);
265 | }
266 | }
267 | return undefined;
268 | }
269 |
270 | protected _encodeMember(
271 | item:
272 | | TS.ConstructorDeclaration
273 | | TS.MethodDeclaration
274 | | TS.MethodSignature
275 | | TS.PropertyDeclaration
276 | | TS.PropertySignature
277 | ): Omit {
278 | const data = {
279 | ...this._encodeItem(item)
280 | // overwrite?: Reference,
281 | } as Partial;
282 |
283 | if (item instanceof TS.MethodDeclaration || item instanceof TS.PropertyDeclaration) {
284 | if (item.isStatic()) data.isStatic = true;
285 | if (item.getScope() === TS.Scope.Protected) data.isProtected = true;
286 | }
287 |
288 | const comment = item.getJsDocs().pop();
289 | if (comment) data.comment = this._encodeComment(comment);
290 |
291 | return data as GD.ApiMember;
292 | }
293 |
294 | protected _encodeConstructor(
295 | item: TS.ConstructorDeclaration,
296 | parent: TS.ClassDeclaration
297 | ): GD.ApiConstructor {
298 | return {
299 | ...this._encodeMember(item),
300 | kind: GD.ApiItemKind.CONSTRUCTOR,
301 | isStatic: undefined,
302 | name: 'constructor',
303 | params: item.getParameters().map((param) => ({
304 | name: param.getName(),
305 | type: this._encodeType(param.getType(), param.getTypeNode()),
306 | optional: param.isOptional() || undefined
307 | })),
308 | returns: this._encodeType(parent.getType())
309 | };
310 | }
311 |
312 | protected _encodeMethod(item: TS.MethodDeclaration | TS.MethodSignature): GD.ApiMethod {
313 | const comment = item.getJsDocs().pop();
314 | return {
315 | ...this._encodeMember(item),
316 | kind: GD.ApiItemKind.METHOD,
317 | params: item.getParameters().map((param) => ({
318 | name: param.getName(),
319 | type: this._encodeType(param.getType(), param.getTypeNode()),
320 | comment: comment ? this._encodeParamComment(comment, param.getName()) : undefined,
321 | optional: param.isOptional() || undefined
322 | })),
323 | returns: this._encodeType(item.getReturnType(), item.getReturnTypeNode()),
324 | returnsComment: comment ? this._encodeReturnsComment(comment) : undefined
325 | };
326 | }
327 |
328 | protected _encodeProperty(item: TS.PropertyDeclaration | TS.PropertySignature): GD.ApiProperty {
329 | return {
330 | ...this._encodeMember(item),
331 | kind: GD.ApiItemKind.PROPERTY,
332 | type: this._encodeType(item.getType(), item.getTypeNode()),
333 | isReadonly: item.isReadonly()
334 | };
335 | }
336 | }
337 |
338 | function getInheritedConstructor(child: TS.ClassDeclaration): TS.ConstructorDeclaration | null {
339 | // TODO: Traverse inheritance tree.
340 | return child.getConstructors()[0] || null;
341 | }
342 |
343 | function getInheritedMembers(
344 | child: TS.ClassDeclaration,
345 | kind: TS.SyntaxKind.MethodDeclaration,
346 | _static: boolean
347 | ): T[];
348 | function getInheritedMembers(
349 | child: TS.ClassDeclaration,
350 | kind: TS.SyntaxKind.PropertyDeclaration,
351 | _static: boolean
352 | ): T[];
353 | function getInheritedMembers(
354 | child: TS.ClassDeclaration,
355 | kind: TS.SyntaxKind.MethodDeclaration | TS.SyntaxKind.PropertyDeclaration,
356 | _static: boolean
357 | ): T[] {
358 | const members: T[] = [];
359 | const memberSet = new Set();
360 |
361 | const baseMembers =
362 | kind === TS.SyntaxKind.MethodDeclaration ? child.getMethods() : child.getProperties();
363 | for (const member of baseMembers) {
364 | if (member.isStatic() !== _static) continue;
365 | memberSet.add(member.getName());
366 | members.push(member as T);
367 | }
368 |
369 | let current = child;
370 | while ((current = current.getBaseClass())) {
371 | const inheritedMembers =
372 | kind === TS.SyntaxKind.MethodDeclaration ? current.getMethods() : current.getProperties();
373 | for (const member of inheritedMembers) {
374 | if (member.isStatic() !== _static) continue;
375 | if (memberSet.has(member.getName())) continue;
376 | memberSet.add(member.getName());
377 | members.push(member as T);
378 | }
379 | }
380 |
381 | return members.sort((a, b) => (a.getName() > b.getName() ? 1 : -1));
382 | }
383 |
384 | let _kindNameCache: Record | undefined;
385 | function getKindName(kind: TS.SyntaxKind): string {
386 | if (!_kindNameCache) {
387 | _kindNameCache = {};
388 | for (const key in TS.SyntaxKind) {
389 | _kindNameCache[TS.SyntaxKind[key]] = key;
390 | }
391 | }
392 | return _kindNameCache[kind];
393 | }
394 |
--------------------------------------------------------------------------------
/packages/docs/static/main.css:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | * * Base
3 | * * ========================================================================== */
4 | /**
5 | * * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using
6 | * * `em` units.
7 | * * 2. Prevent iOS text size adjust after orientation change, without disabling
8 | * * user zoom. */
9 | html {
10 | font-size: 100%;
11 | /* 1 */
12 | -ms-text-size-adjust: 100%;
13 | /* 2 */
14 | -webkit-text-size-adjust: 100%;
15 | /* 2 */
16 | font-family: sans-serif;
17 | }
18 |
19 | /**
20 | * * Address `font-family` inconsistency between `textarea` and other form
21 | * * elements. */
22 | button,
23 | input,
24 | select,
25 | textarea {
26 | font-family: sans-serif;
27 | }
28 |
29 | /**
30 | * * Address margins handled incorrectly in IE 6/7. */
31 | body {
32 | margin: 0;
33 | }
34 |
35 | /* ==========================================================================
36 | * * Links
37 | * * ========================================================================== */
38 | /**
39 | * * Address `outline` inconsistency between Chrome and other browsers. */
40 | a:focus {
41 | outline: thin dotted;
42 | }
43 | a:active,
44 | a:hover {
45 | outline: 0;
46 | }
47 |
48 | /**
49 | * * Improve readability when focused and also mouse hovered in all browsers. */
50 | /* ==========================================================================
51 | * * Typography
52 | * * ========================================================================== */
53 | /**
54 | * * Address font sizes and margins set differently in IE 6/7.
55 | * * Address font sizes within `section` and `article` in Firefox 4+, Safari 5,
56 | * * and Chrome. */
57 | h1 {
58 | font-size: 2em;
59 | margin: 0.67em 0;
60 | }
61 |
62 | h2 {
63 | font-size: 1.5em;
64 | margin: 2em 0 0.83em;
65 | }
66 |
67 | h3 {
68 | font-size: 1.17em;
69 | margin: 1em 0;
70 | }
71 |
72 | h4,
73 | .greendoc-index-panel h3 {
74 | font-size: 1em;
75 | margin: 1.33em 0;
76 | }
77 |
78 | h5 {
79 | font-size: 0.83em;
80 | margin: 1.67em 0;
81 | }
82 |
83 | h6 {
84 | font-size: 0.67em;
85 | margin: 2.33em 0;
86 | }
87 |
88 | /**
89 | * * Address styling not present in IE 7/8/9, Safari 5, and Chrome. */
90 | abbr[title] {
91 | border-bottom: 1px dotted;
92 | }
93 |
94 | /**
95 | * * Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. */
96 | b,
97 | strong {
98 | font-weight: bold;
99 | }
100 |
101 | blockquote {
102 | margin: 1em 40px;
103 | }
104 |
105 | /**
106 | * * Address styling not present in Safari 5 and Chrome. */
107 | dfn {
108 | font-style: italic;
109 | }
110 |
111 | /**
112 | * * Address differences between Firefox and other browsers.
113 | * * Known issue: no IE 6/7 normalization. */
114 | hr {
115 | box-sizing: content-box;
116 | height: 0;
117 | }
118 |
119 | /**
120 | * * Address styling not present in IE 6/7/8/9. */
121 | mark {
122 | background: #ff8;
123 | color: #000;
124 | }
125 |
126 | /**
127 | * * Address margins set differently in IE 6/7. */
128 | p,
129 | pre {
130 | margin: 1em 0;
131 | }
132 |
133 | /**
134 | * * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. */
135 | code,
136 | kbd,
137 | pre,
138 | samp {
139 | font-family: monospace, serif;
140 | _font-family: 'courier new', monospace;
141 | font-size: 1em;
142 | }
143 |
144 | /**
145 | * * Improve readability of pre-formatted text in all browsers. */
146 | pre {
147 | white-space: pre;
148 | white-space: pre-wrap;
149 | word-wrap: break-word;
150 | }
151 |
152 | /**
153 | * * Address CSS quotes not supported in IE 6/7. */
154 | q {
155 | quotes: none;
156 | }
157 | q:before,
158 | q:after {
159 | content: '';
160 | content: none;
161 | }
162 |
163 | /**
164 | * * Address `quotes` property not supported in Safari 4. */
165 | /**
166 | * * Address inconsistent and variable font size in all browsers. */
167 | small {
168 | font-size: 80%;
169 | }
170 |
171 | /**
172 | * * Prevent `sub` and `sup` affecting `line-height` in all browsers. */
173 | sub {
174 | font-size: 75%;
175 | line-height: 0;
176 | position: relative;
177 | vertical-align: baseline;
178 | }
179 |
180 | sup {
181 | font-size: 75%;
182 | line-height: 0;
183 | position: relative;
184 | vertical-align: baseline;
185 | top: -0.5em;
186 | }
187 |
188 | sub {
189 | bottom: -0.25em;
190 | }
191 |
192 | /* ==========================================================================
193 | * * Lists
194 | * * ========================================================================== */
195 | /**
196 | * * Address margins set differently in IE 6/7. */
197 | dl,
198 | menu,
199 | ol,
200 | ul {
201 | margin: 1em 0;
202 | }
203 |
204 | dd {
205 | margin: 0 0 0 40px;
206 | }
207 |
208 | /**
209 | * * Address paddings set differently in IE 6/7. */
210 | menu,
211 | ol,
212 | ul {
213 | padding: 0 0 0 40px;
214 | }
215 |
216 | /**
217 | * * Correct list images handled incorrectly in IE 7. */
218 | nav ul,
219 | nav ol {
220 | list-style: none;
221 | list-style-image: none;
222 | }
223 |
224 | /* ==========================================================================
225 | * * Embedded content
226 | * * ========================================================================== */
227 | /**
228 | * * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3.
229 | * * 2. Improve image quality when scaled in IE 7. */
230 | img {
231 | border: 0;
232 | /* 1 */
233 | -ms-interpolation-mode: bicubic;
234 | }
235 |
236 | /* 2 */
237 | /**
238 | * * Correct overflow displayed oddly in IE 9. */
239 | svg:not(:root) {
240 | overflow: hidden;
241 | }
242 |
243 | /* ==========================================================================
244 | * * Figures
245 | * * ========================================================================== */
246 | /**
247 | * * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. */
248 | figure,
249 | form {
250 | margin: 0;
251 | }
252 |
253 | /* ==========================================================================
254 | * * Forms
255 | * * ========================================================================== */
256 | /**
257 | * * Correct margin displayed oddly in IE 6/7. */
258 | /**
259 | * * Define consistent border, margin, and padding. */
260 | fieldset {
261 | border: 1px solid #c0c0c0;
262 | margin: 0 2px;
263 | padding: 0.35em 0.625em 0.75em;
264 | }
265 |
266 | /**
267 | * * 1. Correct color not being inherited in IE 6/7/8/9.
268 | * * 2. Correct text not wrapping in Firefox 3.
269 | * * 3. Correct alignment displayed oddly in IE 6/7. */
270 | legend {
271 | border: 0;
272 | /* 1 */
273 | padding: 0;
274 | white-space: normal;
275 | /* 2 */
276 | *margin-left: -7px;
277 | }
278 |
279 | /* 3 */
280 | /**
281 | * * 1. Correct font size not being inherited in all browsers.
282 | * * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5,
283 | * * and Chrome.
284 | * * 3. Improve appearance and consistency in all browsers. */
285 | button,
286 | input,
287 | select,
288 | textarea {
289 | font-size: 100%;
290 | /* 1 */
291 | margin: 0;
292 | /* 2 */
293 | vertical-align: baseline;
294 | /* 3 */
295 | *vertical-align: middle;
296 | }
297 |
298 | /* 3 */
299 | /**
300 | * * Address Firefox 3+ setting `line-height` on `input` using `!important` in
301 | * * the UA stylesheet. */
302 | button,
303 | input {
304 | line-height: normal;
305 | }
306 |
307 | /**
308 | * * Address inconsistent `text-transform` inheritance for `button` and `select`.
309 | * * All other form control elements do not inherit `text-transform` values.
310 | * * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
311 | * * Correct `select` style inheritance in Firefox 4+ and Opera. */
312 | button,
313 | select {
314 | text-transform: none;
315 | }
316 |
317 | /**
318 | * * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
319 | * * and `video` controls.
320 | * * 2. Correct inability to style clickable `input` types in iOS.
321 | * * 3. Improve usability and consistency of cursor style between image-type
322 | * * `input` and others.
323 | * * 4. Remove inner spacing in IE 7 without affecting normal text inputs.
324 | * * Known issue: inner spacing remains in IE 6. */
325 | button,
326 | html input[type='button'] {
327 | -webkit-appearance: button;
328 | /* 2 */
329 | cursor: pointer;
330 | /* 3 */
331 | *overflow: visible;
332 | }
333 |
334 | /* 4 */
335 | input[type='reset'],
336 | input[type='submit'] {
337 | -webkit-appearance: button;
338 | /* 2 */
339 | cursor: pointer;
340 | /* 3 */
341 | *overflow: visible;
342 | }
343 |
344 | /* 4 */
345 | /**
346 | * * Re-set default cursor for disabled elements. */
347 | button[disabled],
348 | html input[disabled] {
349 | cursor: default;
350 | }
351 |
352 | /**
353 | * * 1. Address box sizing set to content-box in IE 8/9.
354 | * * 2. Remove excess padding in IE 8/9.
355 | * * 3. Remove excess padding in IE 7.
356 | * * Known issue: excess padding remains in IE 6. */
357 | input {
358 | /* 3 */
359 | }
360 | input[type='checkbox'],
361 | input[type='radio'] {
362 | box-sizing: border-box;
363 | /* 1 */
364 | padding: 0;
365 | /* 2 */
366 | *height: 13px;
367 | /* 3 */
368 | *width: 13px;
369 | }
370 | input[type='search'] {
371 | -webkit-appearance: textfield;
372 | /* 1 */
373 | /* 2 */
374 | box-sizing: content-box;
375 | }
376 | input[type='search']::-webkit-search-cancel-button,
377 | input[type='search']::-webkit-search-decoration {
378 | -webkit-appearance: none;
379 | }
380 |
381 | /**
382 | * * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
383 | * * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
384 | * * (include `-moz` to future-proof). */
385 | /**
386 | * * Remove inner padding and search cancel button in Safari 5 and Chrome
387 | * * on OS X. */
388 | /**
389 | * * Remove inner padding and border in Firefox 3+. */
390 | button::-moz-focus-inner,
391 | input::-moz-focus-inner {
392 | border: 0;
393 | padding: 0;
394 | }
395 |
396 | /**
397 | * * 1. Remove default vertical scrollbar in IE 6/7/8/9.
398 | * * 2. Improve readability and alignment in all browsers. */
399 | textarea {
400 | overflow: auto;
401 | /* 1 */
402 | vertical-align: top;
403 | }
404 |
405 | /* 2 */
406 | /* ==========================================================================
407 | * * Tables
408 | * * ========================================================================== */
409 | /**
410 | * * Remove most spacing between table cells. */
411 | table {
412 | border-collapse: collapse;
413 | border-spacing: 0;
414 | }
415 |
416 | /* *
417 | * *Visual Studio-like style based on original C# coloring by Jason Diamond */
418 | .hljs {
419 | display: inline-block;
420 | padding: 0.5em;
421 | background: white;
422 | color: black;
423 | }
424 |
425 | .hljs-comment,
426 | .hljs-annotation,
427 | .hljs-template_comment,
428 | .diff .hljs-header,
429 | .hljs-chunk,
430 | .apache .hljs-cbracket {
431 | color: #008000;
432 | }
433 |
434 | .hljs-keyword,
435 | .hljs-id,
436 | .hljs-built_in,
437 | .css .smalltalk .hljs-class,
438 | .hljs-winutils,
439 | .bash .hljs-variable,
440 | .tex .hljs-command,
441 | .hljs-request,
442 | .hljs-status,
443 | .nginx .hljs-title {
444 | color: #00f;
445 | }
446 |
447 | .xml .hljs-tag {
448 | color: #00f;
449 | }
450 | .xml .hljs-tag .hljs-value {
451 | color: #00f;
452 | }
453 |
454 | .hljs-string,
455 | .hljs-title,
456 | .hljs-parent,
457 | .hljs-tag .hljs-value,
458 | .hljs-rules .hljs-value {
459 | color: #a31515;
460 | }
461 |
462 | .ruby .hljs-symbol {
463 | color: #a31515;
464 | }
465 | .ruby .hljs-symbol .hljs-string {
466 | color: #a31515;
467 | }
468 |
469 | .hljs-template_tag,
470 | .django .hljs-variable,
471 | .hljs-addition,
472 | .hljs-flow,
473 | .hljs-stream,
474 | .apache .hljs-tag,
475 | .hljs-date,
476 | .tex .hljs-formula,
477 | .coffeescript .hljs-attribute {
478 | color: #a31515;
479 | }
480 |
481 | .ruby .hljs-string,
482 | .hljs-decorator,
483 | .hljs-filter .hljs-argument,
484 | .hljs-localvars,
485 | .hljs-array,
486 | .hljs-attr_selector,
487 | .hljs-pseudo,
488 | .hljs-pi,
489 | .hljs-doctype,
490 | .hljs-deletion,
491 | .hljs-envvar,
492 | .hljs-shebang,
493 | .hljs-preprocessor,
494 | .hljs-pragma,
495 | .userType,
496 | .apache .hljs-sqbracket,
497 | .nginx .hljs-built_in,
498 | .tex .hljs-special,
499 | .hljs-prompt {
500 | color: #2b91af;
501 | }
502 |
503 | .hljs-phpdoc,
504 | .hljs-javadoc,
505 | .hljs-xmlDocTag {
506 | color: #808080;
507 | }
508 |
509 | .vhdl .hljs-typename {
510 | font-weight: bold;
511 | }
512 | .vhdl .hljs-string {
513 | color: #666666;
514 | }
515 | .vhdl .hljs-literal {
516 | color: #a31515;
517 | }
518 | .vhdl .hljs-attribute {
519 | color: #00b0e8;
520 | }
521 |
522 | .xml .hljs-attribute {
523 | color: #f00;
524 | }
525 |
526 | ul.greendoc-descriptions > li > :first-child,
527 | .greendoc-panel > :first-child,
528 | .col > :first-child,
529 | .col-11 > :first-child,
530 | .col-10 > :first-child,
531 | .col-9 > :first-child,
532 | .col-8 > :first-child,
533 | .col-7 > :first-child,
534 | .col-6 > :first-child,
535 | .col-5 > :first-child,
536 | .col-4 > :first-child,
537 | .col-3 > :first-child,
538 | .col-2 > :first-child,
539 | .col-1 > :first-child,
540 | ul.greendoc-descriptions > li > :first-child > :first-child,
541 | .greendoc-panel > :first-child > :first-child,
542 | .col > :first-child > :first-child,
543 | .col-11 > :first-child > :first-child,
544 | .col-10 > :first-child > :first-child,
545 | .col-9 > :first-child > :first-child,
546 | .col-8 > :first-child > :first-child,
547 | .col-7 > :first-child > :first-child,
548 | .col-6 > :first-child > :first-child,
549 | .col-5 > :first-child > :first-child,
550 | .col-4 > :first-child > :first-child,
551 | .col-3 > :first-child > :first-child,
552 | .col-2 > :first-child > :first-child,
553 | .col-1 > :first-child > :first-child,
554 | ul.greendoc-descriptions > li > :first-child > :first-child > :first-child,
555 | .greendoc-panel > :first-child > :first-child > :first-child,
556 | .col > :first-child > :first-child > :first-child,
557 | .col-11 > :first-child > :first-child > :first-child,
558 | .col-10 > :first-child > :first-child > :first-child,
559 | .col-9 > :first-child > :first-child > :first-child,
560 | .col-8 > :first-child > :first-child > :first-child,
561 | .col-7 > :first-child > :first-child > :first-child,
562 | .col-6 > :first-child > :first-child > :first-child,
563 | .col-5 > :first-child > :first-child > :first-child,
564 | .col-4 > :first-child > :first-child > :first-child,
565 | .col-3 > :first-child > :first-child > :first-child,
566 | .col-2 > :first-child > :first-child > :first-child,
567 | .col-1 > :first-child > :first-child > :first-child {
568 | margin-top: 0;
569 | }
570 | ul.greendoc-descriptions > li > :last-child,
571 | .greendoc-panel > :last-child,
572 | .col > :last-child,
573 | .col-11 > :last-child,
574 | .col-10 > :last-child,
575 | .col-9 > :last-child,
576 | .col-8 > :last-child,
577 | .col-7 > :last-child,
578 | .col-6 > :last-child,
579 | .col-5 > :last-child,
580 | .col-4 > :last-child,
581 | .col-3 > :last-child,
582 | .col-2 > :last-child,
583 | .col-1 > :last-child,
584 | ul.greendoc-descriptions > li > :last-child > :last-child,
585 | .greendoc-panel > :last-child > :last-child,
586 | .col > :last-child > :last-child,
587 | .col-11 > :last-child > :last-child,
588 | .col-10 > :last-child > :last-child,
589 | .col-9 > :last-child > :last-child,
590 | .col-8 > :last-child > :last-child,
591 | .col-7 > :last-child > :last-child,
592 | .col-6 > :last-child > :last-child,
593 | .col-5 > :last-child > :last-child,
594 | .col-4 > :last-child > :last-child,
595 | .col-3 > :last-child > :last-child,
596 | .col-2 > :last-child > :last-child,
597 | .col-1 > :last-child > :last-child,
598 | ul.greendoc-descriptions > li > :last-child > :last-child > :last-child,
599 | .greendoc-panel > :last-child > :last-child > :last-child,
600 | .col > :last-child > :last-child > :last-child,
601 | .col-11 > :last-child > :last-child > :last-child,
602 | .col-10 > :last-child > :last-child > :last-child,
603 | .col-9 > :last-child > :last-child > :last-child,
604 | .col-8 > :last-child > :last-child > :last-child,
605 | .col-7 > :last-child > :last-child > :last-child,
606 | .col-6 > :last-child > :last-child > :last-child,
607 | .col-5 > :last-child > :last-child > :last-child,
608 | .col-4 > :last-child > :last-child > :last-child,
609 | .col-3 > :last-child > :last-child > :last-child,
610 | .col-2 > :last-child > :last-child > :last-child,
611 | .col-1 > :last-child > :last-child > :last-child {
612 | margin-bottom: 0;
613 | }
614 |
615 | .container {
616 | max-width: 1200px;
617 | margin: 0 auto;
618 | padding: 0 40px;
619 | }
620 | @media (max-width: 640px) {
621 | .container {
622 | padding: 0 20px;
623 | }
624 | }
625 |
626 | .container-main {
627 | padding-bottom: 200px;
628 | }
629 |
630 | .row {
631 | display: -ms-flexbox;
632 | display: flex;
633 | position: relative;
634 | margin: 0 -10px;
635 | }
636 | .row:after {
637 | visibility: hidden;
638 | display: block;
639 | content: '';
640 | clear: both;
641 | height: 0;
642 | }
643 |
644 | .col,
645 | .col-11,
646 | .col-10,
647 | .col-9,
648 | .col-8,
649 | .col-7,
650 | .col-6,
651 | .col-5,
652 | .col-4,
653 | .col-3,
654 | .col-2,
655 | .col-1 {
656 | box-sizing: border-box;
657 | float: left;
658 | padding: 0 10px;
659 | }
660 |
661 | .col-1 {
662 | width: 8.3333333333%;
663 | }
664 |
665 | .offset-1 {
666 | margin-left: 8.3333333333%;
667 | }
668 |
669 | .col-2 {
670 | width: 16.6666666667%;
671 | }
672 |
673 | .offset-2 {
674 | margin-left: 16.6666666667%;
675 | }
676 |
677 | .col-3 {
678 | width: 25%;
679 | }
680 |
681 | .offset-3 {
682 | margin-left: 25%;
683 | }
684 |
685 | .col-4 {
686 | width: 33.3333333333%;
687 | }
688 |
689 | .offset-4 {
690 | margin-left: 33.3333333333%;
691 | }
692 |
693 | .col-5 {
694 | width: 41.6666666667%;
695 | }
696 |
697 | .offset-5 {
698 | margin-left: 41.6666666667%;
699 | }
700 |
701 | .col-6 {
702 | width: 50%;
703 | }
704 |
705 | .offset-6 {
706 | margin-left: 50%;
707 | }
708 |
709 | .col-7 {
710 | width: 58.3333333333%;
711 | }
712 |
713 | .offset-7 {
714 | margin-left: 58.3333333333%;
715 | }
716 |
717 | .col-8 {
718 | width: 66.6666666667%;
719 | }
720 |
721 | .offset-8 {
722 | margin-left: 66.6666666667%;
723 | }
724 |
725 | .col-9 {
726 | width: 75%;
727 | }
728 |
729 | .offset-9 {
730 | margin-left: 75%;
731 | }
732 |
733 | .col-10 {
734 | width: 83.3333333333%;
735 | }
736 |
737 | .offset-10 {
738 | margin-left: 83.3333333333%;
739 | }
740 |
741 | .col-11 {
742 | width: 91.6666666667%;
743 | }
744 |
745 | .offset-11 {
746 | margin-left: 91.6666666667%;
747 | }
748 |
749 | @keyframes fade-in {
750 | from {
751 | opacity: 0;
752 | }
753 | to {
754 | opacity: 1;
755 | }
756 | }
757 | @keyframes fade-out {
758 | from {
759 | opacity: 1;
760 | visibility: visible;
761 | }
762 | to {
763 | opacity: 0;
764 | }
765 | }
766 | @keyframes fade-in-delayed {
767 | 0% {
768 | opacity: 0;
769 | }
770 | 33% {
771 | opacity: 0;
772 | }
773 | 100% {
774 | opacity: 1;
775 | }
776 | }
777 | @keyframes fade-out-delayed {
778 | 0% {
779 | opacity: 1;
780 | visibility: visible;
781 | }
782 | 66% {
783 | opacity: 0;
784 | }
785 | 100% {
786 | opacity: 0;
787 | }
788 | }
789 | @keyframes shift-to-left {
790 | from {
791 | transform: translate(0, 0);
792 | }
793 | to {
794 | transform: translate(-25%, 0);
795 | }
796 | }
797 | @keyframes unshift-to-left {
798 | from {
799 | transform: translate(-25%, 0);
800 | }
801 | to {
802 | transform: translate(0, 0);
803 | }
804 | }
805 | @keyframes pop-in-from-right {
806 | from {
807 | transform: translate(100%, 0);
808 | }
809 | to {
810 | transform: translate(0, 0);
811 | }
812 | }
813 | @keyframes pop-out-to-right {
814 | from {
815 | transform: translate(0, 0);
816 | visibility: visible;
817 | }
818 | to {
819 | transform: translate(100%, 0);
820 | }
821 | }
822 | body {
823 | background: #fdfdfd;
824 | font-family: 'Segoe UI', sans-serif;
825 | font-size: 16px;
826 | color: #222;
827 | }
828 |
829 | a {
830 | color: #4da6ff;
831 | text-decoration: none;
832 | }
833 | a:hover {
834 | text-decoration: underline;
835 | }
836 |
837 | code,
838 | pre {
839 | font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
840 | padding: 0.2em;
841 | margin: 0;
842 | font-size: 14px;
843 | background-color: rgba(0, 0, 0, 0.04);
844 | }
845 |
846 | pre {
847 | padding: 2em;
848 | }
849 | pre code {
850 | padding: 0;
851 | font-size: 100%;
852 | background-color: transparent;
853 | }
854 |
855 | .greendoc-typography {
856 | line-height: 1.333em;
857 | }
858 | .greendoc-typography ul {
859 | list-style: square;
860 | padding: 0 0 0 20px;
861 | margin: 0;
862 | }
863 | .greendoc-typography h4,
864 | .greendoc-typography .greendoc-index-panel h3,
865 | .greendoc-index-panel .greendoc-typography h3,
866 | .greendoc-typography h5,
867 | .greendoc-typography h6 {
868 | font-size: 1em;
869 | margin: 0;
870 | }
871 | .greendoc-typography h5,
872 | .greendoc-typography h6 {
873 | font-weight: normal;
874 | }
875 | .greendoc-typography p,
876 | .greendoc-typography ul,
877 | .greendoc-typography ol {
878 | margin: 1em 0;
879 | }
880 |
881 | @media (min-width: 901px) and (max-width: 1024px) {
882 | html.default .col-content {
883 | width: 72%;
884 | }
885 | html.default .col-menu {
886 | width: 28%;
887 | }
888 | html.default .greendoc-navigation {
889 | padding-left: 10px;
890 | }
891 | }
892 | @media (max-width: 900px) {
893 | html.default .col-content {
894 | float: none;
895 | width: 100%;
896 | }
897 | html.default .col-menu {
898 | position: fixed !important;
899 | overflow: auto;
900 | -webkit-overflow-scrolling: touch;
901 | z-index: 1024;
902 | top: 0 !important;
903 | bottom: 0 !important;
904 | left: auto !important;
905 | right: 0 !important;
906 | width: 100%;
907 | padding: 20px 20px 0 0;
908 | max-width: 450px;
909 | visibility: hidden;
910 | background-color: #fff;
911 | transform: translate(100%, 0);
912 | }
913 | html.default .col-menu > *:last-child {
914 | padding-bottom: 20px;
915 | }
916 | html.default .overlay {
917 | content: '';
918 | display: block;
919 | position: fixed;
920 | z-index: 1023;
921 | top: 0;
922 | left: 0;
923 | right: 0;
924 | bottom: 0;
925 | background-color: rgba(0, 0, 0, 0.75);
926 | visibility: hidden;
927 | }
928 | html.default.to-has-menu .overlay {
929 | animation: fade-in 0.4s;
930 | }
931 | html.default.to-has-menu header,
932 | html.default.to-has-menu footer,
933 | html.default.to-has-menu .col-content {
934 | animation: shift-to-left 0.4s;
935 | }
936 | html.default.to-has-menu .col-menu {
937 | animation: pop-in-from-right 0.4s;
938 | }
939 | html.default.from-has-menu .overlay {
940 | animation: fade-out 0.4s;
941 | }
942 | html.default.from-has-menu header,
943 | html.default.from-has-menu footer,
944 | html.default.from-has-menu .col-content {
945 | animation: unshift-to-left 0.4s;
946 | }
947 | html.default.from-has-menu .col-menu {
948 | animation: pop-out-to-right 0.4s;
949 | }
950 | html.default.has-menu body {
951 | overflow: hidden;
952 | }
953 | html.default.has-menu .overlay {
954 | visibility: visible;
955 | }
956 | html.default.has-menu header,
957 | html.default.has-menu footer,
958 | html.default.has-menu .col-content {
959 | transform: translate(-25%, 0);
960 | }
961 | html.default.has-menu .col-menu {
962 | visibility: visible;
963 | transform: translate(0, 0);
964 | }
965 | }
966 |
967 | .greendoc-page-title {
968 | padding: 70px 0 20px 0;
969 | margin: 0 0 40px 0;
970 | background: #fff;
971 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.35);
972 | }
973 | .greendoc-page-title h1 {
974 | margin: 0;
975 | }
976 |
977 | .greendoc-breadcrumb {
978 | margin: 0;
979 | padding: 0;
980 | color: #808080;
981 | }
982 | .greendoc-breadcrumb a {
983 | color: #808080;
984 | text-decoration: none;
985 | }
986 | .greendoc-breadcrumb a:hover {
987 | text-decoration: underline;
988 | }
989 | .greendoc-breadcrumb li {
990 | display: inline;
991 | }
992 | .greendoc-breadcrumb li:after {
993 | content: ' / ';
994 | }
995 |
996 | html.minimal .container {
997 | margin: 0;
998 | }
999 | html.minimal .container-main {
1000 | padding-top: 50px;
1001 | padding-bottom: 0;
1002 | }
1003 | html.minimal .content-wrap {
1004 | padding-left: 300px;
1005 | }
1006 | html.minimal .greendoc-navigation {
1007 | position: fixed !important;
1008 | overflow: auto;
1009 | -webkit-overflow-scrolling: touch;
1010 | box-sizing: border-box;
1011 | z-index: 1;
1012 | left: 0;
1013 | top: 40px;
1014 | bottom: 0;
1015 | width: 300px;
1016 | padding: 20px;
1017 | margin: 0;
1018 | }
1019 | html.minimal .greendoc-member .greendoc-member {
1020 | margin-left: 0;
1021 | }
1022 | html.minimal .greendoc-page-toolbar {
1023 | position: fixed;
1024 | z-index: 2;
1025 | }
1026 | html.minimal footer {
1027 | background-color: transparent;
1028 | }
1029 | html.minimal footer .container {
1030 | padding: 0;
1031 | }
1032 | html.minimal .greendoc-generator {
1033 | padding: 0;
1034 | }
1035 | @media (max-width: 900px) {
1036 | html.minimal .greendoc-navigation {
1037 | display: none;
1038 | }
1039 | html.minimal .content-wrap {
1040 | padding-left: 0;
1041 | }
1042 | }
1043 |
1044 | dl.greendoc-comment-tags {
1045 | overflow: hidden;
1046 | }
1047 | dl.greendoc-comment-tags dt {
1048 | float: left;
1049 | padding: 1px 5px;
1050 | margin: 0 10px 0 0;
1051 | border-radius: 4px;
1052 | border: 1px solid #808080;
1053 | color: #808080;
1054 | font-size: 0.8em;
1055 | font-weight: normal;
1056 | }
1057 | dl.greendoc-comment-tags dd {
1058 | margin: 0 0 10px 0;
1059 | }
1060 | dl.greendoc-comment-tags dd:before,
1061 | dl.greendoc-comment-tags dd:after {
1062 | display: table;
1063 | content: ' ';
1064 | }
1065 | dl.greendoc-comment-tags dd pre,
1066 | dl.greendoc-comment-tags dd:after {
1067 | clear: both;
1068 | }
1069 | dl.greendoc-comment-tags p {
1070 | margin: 0;
1071 | }
1072 |
1073 | /** TODO: Merge with above? */
1074 | .tags {
1075 | list-style: none;
1076 | margin: 0;
1077 | padding: 0;
1078 | }
1079 | .tag {
1080 | display: inline-block;
1081 | padding: 0.2em 0.5em;
1082 | background: rgba(0, 0, 0, 0.04);
1083 | height: 14px;
1084 | font-size: 0.8em;
1085 | font-family: monospace;
1086 | border: 1px solid #c0c0c0;
1087 | border-radius: 7px;
1088 | margin-right: 0.2em;
1089 | }
1090 | .tag[data-gd-tag='alpha'],
1091 | .tag[data-gd-tag='experimental'] {
1092 | background-color: orangered;
1093 | color: #fff;
1094 | }
1095 | .tag[data-gd-tag='beta'] {
1096 | background-color: goldenrod;
1097 | color: #fff;
1098 | }
1099 | .tag[data-gd-tag='deprecated'] {
1100 | background-color: crimson;
1101 | color: #fff;
1102 | }
1103 |
1104 | .greendoc-panel.greendoc-comment .lead {
1105 | font-size: 1.1em;
1106 | line-height: 1.333em;
1107 | margin-bottom: 2em;
1108 | }
1109 | .greendoc-panel.greendoc-comment .lead:last-child {
1110 | margin-bottom: 0;
1111 | }
1112 |
1113 | .toggle-protected .greendoc-is-private {
1114 | display: none;
1115 | }
1116 |
1117 | .toggle-public .greendoc-is-private,
1118 | .toggle-public .greendoc-is-protected,
1119 | .toggle-public .greendoc-is-private-protected {
1120 | display: none;
1121 | }
1122 |
1123 | .toggle-inherited .greendoc-is-inherited {
1124 | display: none;
1125 | }
1126 |
1127 | .toggle-only-exported .greendoc-is-not-exported {
1128 | display: none;
1129 | }
1130 |
1131 | .toggle-externals .greendoc-is-external {
1132 | display: none;
1133 | }
1134 |
1135 | footer {
1136 | border-top: 1px solid #eee;
1137 | background-color: #fff;
1138 | }
1139 | footer.with-border-bottom {
1140 | border-bottom: 1px solid #eee;
1141 | }
1142 | footer .greendoc-legend-group {
1143 | font-size: 0;
1144 | }
1145 | footer .greendoc-legend {
1146 | display: inline-block;
1147 | width: 25%;
1148 | padding: 0;
1149 | font-size: 16px;
1150 | list-style: none;
1151 | line-height: 1.333em;
1152 | vertical-align: top;
1153 | }
1154 | @media (max-width: 900px) {
1155 | footer .greendoc-legend {
1156 | width: 50%;
1157 | }
1158 | }
1159 |
1160 | .greendoc-hierarchy {
1161 | list-style: square;
1162 | padding: 0 0 0 20px;
1163 | margin: 0;
1164 | }
1165 | .greendoc-hierarchy .target {
1166 | font-weight: bold;
1167 | }
1168 |
1169 | .greendoc-index-panel .greendoc-index-content {
1170 | margin-bottom: -30px !important;
1171 | }
1172 | .greendoc-index-panel .greendoc-index-section {
1173 | margin-bottom: 30px !important;
1174 | }
1175 | .greendoc-index-panel h3 {
1176 | margin: 0 -20px 10px -20px;
1177 | padding: 0 20px 10px 20px;
1178 | border-bottom: 1px solid #eee;
1179 | }
1180 | .greendoc-index-panel ul.greendoc-index-list {
1181 | -moz-column-count: 3;
1182 | -ms-column-count: 3;
1183 | -o-column-count: 3;
1184 | column-count: 3;
1185 | -moz-column-gap: 20px;
1186 | -ms-column-gap: 20px;
1187 | -o-column-gap: 20px;
1188 | column-gap: 20px;
1189 | padding: 0;
1190 | list-style: none;
1191 | line-height: 1.333em;
1192 | }
1193 | @media (max-width: 900px) {
1194 | .greendoc-index-panel ul.greendoc-index-list {
1195 | -moz-column-count: 1;
1196 | -ms-column-count: 1;
1197 | -o-column-count: 1;
1198 | column-count: 1;
1199 | }
1200 | }
1201 | @media (min-width: 901px) and (max-width: 1024px) {
1202 | .greendoc-index-panel ul.greendoc-index-list {
1203 | -moz-column-count: 2;
1204 | -ms-column-count: 2;
1205 | -o-column-count: 2;
1206 | column-count: 2;
1207 | }
1208 | }
1209 | .greendoc-index-panel ul.greendoc-index-list li {
1210 | -webkit-page-break-inside: avoid;
1211 | -moz-page-break-inside: avoid;
1212 | -ms-page-break-inside: avoid;
1213 | -o-page-break-inside: avoid;
1214 | page-break-inside: avoid;
1215 | }
1216 | .greendoc-index-panel a,
1217 | .greendoc-index-panel .greendoc-parent-kind-module a {
1218 | color: #9600ff;
1219 | }
1220 | .greendoc-index-panel .greendoc-parent-kind-interface a {
1221 | color: #7da01f;
1222 | }
1223 | .greendoc-index-panel .greendoc-parent-kind-enum a {
1224 | color: #cc9900;
1225 | }
1226 | .greendoc-index-panel .greendoc-parent-kind-class a {
1227 | color: #4da6ff;
1228 | }
1229 | .greendoc-index-panel .greendoc-kind-module a {
1230 | color: #9600ff;
1231 | }
1232 | .greendoc-index-panel .greendoc-kind-interface a {
1233 | color: #7da01f;
1234 | }
1235 | .greendoc-index-panel .greendoc-kind-enum a {
1236 | color: #cc9900;
1237 | }
1238 | .greendoc-index-panel .greendoc-kind-class a {
1239 | color: #4da6ff;
1240 | }
1241 | .greendoc-index-panel .greendoc-is-private a {
1242 | color: #808080;
1243 | }
1244 |
1245 | .greendoc-flag {
1246 | display: inline-block;
1247 | padding: 1px 5px;
1248 | border-radius: 4px;
1249 | color: #fff;
1250 | background-color: #808080;
1251 | text-indent: 0;
1252 | font-size: 14px;
1253 | font-weight: normal;
1254 | }
1255 |
1256 | .greendoc-anchor {
1257 | position: absolute;
1258 | top: -100px;
1259 | }
1260 |
1261 | .greendoc-member {
1262 | position: relative;
1263 | }
1264 | .greendoc-member .greendoc-anchor + h3 {
1265 | margin-top: 0;
1266 | margin-bottom: 0;
1267 | border-bottom: none;
1268 | }
1269 |
1270 | .greendoc-navigation {
1271 | margin: 0 0 0 40px;
1272 | }
1273 | .greendoc-navigation a {
1274 | display: block;
1275 | padding-top: 2px;
1276 | padding-bottom: 2px;
1277 | border-left: 2px solid transparent;
1278 | color: #222;
1279 | text-decoration: none;
1280 | transition: border-left-color 0.1s;
1281 | }
1282 | .greendoc-navigation a:hover {
1283 | text-decoration: underline;
1284 | }
1285 | .greendoc-navigation ul {
1286 | margin: 0;
1287 | padding: 0;
1288 | list-style: none;
1289 | }
1290 | .greendoc-navigation li {
1291 | padding: 0;
1292 | }
1293 | .greendoc-navigation > section:not(:last-child) {
1294 | padding-bottom: 1em;
1295 | border-bottom: 1px solid #ccc;
1296 | }
1297 |
1298 | .greendoc-navigation.primary {
1299 | padding-bottom: 40px;
1300 | }
1301 | .greendoc-navigation.primary a {
1302 | display: block;
1303 | padding-top: 6px;
1304 | padding-bottom: 6px;
1305 | }
1306 | .greendoc-navigation.primary ul li a {
1307 | padding-left: 5px;
1308 | }
1309 | .greendoc-navigation.primary ul li li a {
1310 | padding-left: 25px;
1311 | }
1312 | .greendoc-navigation.primary ul li li li a {
1313 | padding-left: 45px;
1314 | }
1315 | .greendoc-navigation.primary ul li li li li a {
1316 | padding-left: 65px;
1317 | }
1318 | .greendoc-navigation.primary ul li li li li li a {
1319 | padding-left: 85px;
1320 | }
1321 | .greendoc-navigation.primary ul li li li li li li a {
1322 | padding-left: 105px;
1323 | }
1324 | .greendoc-navigation.primary > ul {
1325 | border-bottom: 1px solid #eee;
1326 | }
1327 | .greendoc-navigation.primary li {
1328 | border-top: 1px solid #eee;
1329 | }
1330 | .greendoc-navigation.primary li.current > a {
1331 | font-weight: bold;
1332 | }
1333 | .greendoc-navigation.primary li.label span {
1334 | display: block;
1335 | padding: 20px 0 6px 5px;
1336 | color: #808080;
1337 | }
1338 | .greendoc-navigation.primary li.globals + li > span,
1339 | .greendoc-navigation.primary li.globals + li > a {
1340 | padding-top: 20px;
1341 | }
1342 |
1343 | .greendoc-navigation.secondary {
1344 | max-height: calc(100vh - 1rem - 40px);
1345 | overflow: auto;
1346 | position: -webkit-sticky;
1347 | position: sticky;
1348 | top: calc(0.5rem + 40px);
1349 | transition: 0.3s;
1350 | }
1351 | .greendoc-navigation.secondary.greendoc-navigation--toolbar-hide {
1352 | max-height: calc(100vh - 1rem);
1353 | top: 0.5rem;
1354 | }
1355 | .greendoc-navigation.secondary ul {
1356 | transition: opacity 0.2s;
1357 | }
1358 | .greendoc-navigation.secondary ul li a {
1359 | padding-left: 25px;
1360 | }
1361 | .greendoc-navigation.secondary ul li li a {
1362 | padding-left: 45px;
1363 | }
1364 | .greendoc-navigation.secondary ul li li li a {
1365 | padding-left: 65px;
1366 | }
1367 | .greendoc-navigation.secondary ul li li li li a {
1368 | padding-left: 85px;
1369 | }
1370 | .greendoc-navigation.secondary ul li li li li li a {
1371 | padding-left: 105px;
1372 | }
1373 | .greendoc-navigation.secondary ul li li li li li li a {
1374 | padding-left: 125px;
1375 | }
1376 | .greendoc-navigation.secondary ul.current a {
1377 | border-left-color: #eee;
1378 | }
1379 | .greendoc-navigation.secondary li.focus > a,
1380 | .greendoc-navigation.secondary ul.current li.focus > a {
1381 | border-left-color: #000;
1382 | }
1383 | .greendoc-navigation.secondary li.current {
1384 | margin-top: 20px;
1385 | margin-bottom: 20px;
1386 | border-left-color: #eee;
1387 | }
1388 | .greendoc-navigation.secondary li.current > a {
1389 | font-weight: bold;
1390 | }
1391 |
1392 | @media (min-width: 901px) {
1393 | .menu-sticky-wrap {
1394 | position: static;
1395 | }
1396 | }
1397 |
1398 | .greendoc-panel {
1399 | margin: 20px 0;
1400 | padding: 20px;
1401 | background-color: #fff;
1402 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.25);
1403 | }
1404 | .greendoc-panel:empty {
1405 | display: none;
1406 | }
1407 | .greendoc-panel > h1,
1408 | .greendoc-panel > h2,
1409 | .greendoc-panel > h3 {
1410 | margin: 1.5em -20px 10px -20px;
1411 | padding: 0 20px 10px 20px;
1412 | border-bottom: 1px solid #eee;
1413 | }
1414 | .greendoc-panel > h1.greendoc-before-signature,
1415 | .greendoc-panel > h2.greendoc-before-signature,
1416 | .greendoc-panel > h3.greendoc-before-signature {
1417 | margin-bottom: 0;
1418 | border-bottom: 0;
1419 | }
1420 | .greendoc-panel table {
1421 | display: block;
1422 | width: 100%;
1423 | overflow: auto;
1424 | margin-top: 10px;
1425 | word-break: normal;
1426 | word-break: keep-all;
1427 | }
1428 | .greendoc-panel table th {
1429 | font-weight: bold;
1430 | }
1431 | .greendoc-panel table th,
1432 | .greendoc-panel table td {
1433 | padding: 6px 13px;
1434 | border: 1px solid #ddd;
1435 | }
1436 | .greendoc-panel table tr {
1437 | background-color: #fff;
1438 | border-top: 1px solid #ccc;
1439 | }
1440 | .greendoc-panel table tr:nth-child(2n) {
1441 | background-color: #f8f8f8;
1442 | }
1443 |
1444 | .greendoc-panel-group {
1445 | margin: 60px 0;
1446 | }
1447 | .greendoc-panel-group > h1,
1448 | .greendoc-panel-group > h2,
1449 | .greendoc-panel-group > h3 {
1450 | padding-left: 20px;
1451 | padding-right: 20px;
1452 | }
1453 |
1454 | #greendoc-search {
1455 | transition: background-color 0.2s;
1456 | }
1457 | #greendoc-search .title {
1458 | position: relative;
1459 | z-index: 2;
1460 | }
1461 | #greendoc-search .field {
1462 | position: absolute;
1463 | left: 0;
1464 | top: 0;
1465 | right: 40px;
1466 | height: 40px;
1467 | }
1468 | #greendoc-search .field input {
1469 | box-sizing: border-box;
1470 | position: relative;
1471 | top: -50px;
1472 | z-index: 1;
1473 | width: 100%;
1474 | padding: 0 10px;
1475 | opacity: 0;
1476 | outline: 0;
1477 | border: 0;
1478 | background: transparent;
1479 | color: #222;
1480 | }
1481 | #greendoc-search .field label {
1482 | position: absolute;
1483 | overflow: hidden;
1484 | right: -40px;
1485 | }
1486 | #greendoc-search .field input,
1487 | #greendoc-search .title {
1488 | transition: opacity 0.2s;
1489 | }
1490 | #greendoc-search .results {
1491 | position: absolute;
1492 | visibility: hidden;
1493 | top: 40px;
1494 | width: 100%;
1495 | margin: 0;
1496 | padding: 0;
1497 | list-style: none;
1498 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.25);
1499 | }
1500 | #greendoc-search .results li {
1501 | padding: 0 10px;
1502 | background-color: #fdfdfd;
1503 | }
1504 | #greendoc-search .results li:nth-child(even) {
1505 | background-color: #fff;
1506 | }
1507 | #greendoc-search .results li.state {
1508 | display: none;
1509 | }
1510 | #greendoc-search .results li.current,
1511 | #greendoc-search .results li:hover {
1512 | background-color: #eee;
1513 | }
1514 | #greendoc-search .results a {
1515 | display: block;
1516 | }
1517 | #greendoc-search .results a:before {
1518 | top: 10px;
1519 | }
1520 | #greendoc-search .results span.parent {
1521 | color: #808080;
1522 | font-weight: normal;
1523 | }
1524 | #greendoc-search.has-focus {
1525 | background-color: #eee;
1526 | }
1527 | #greendoc-search.has-focus .field input {
1528 | top: 0;
1529 | opacity: 1;
1530 | }
1531 | #greendoc-search.has-focus .title {
1532 | z-index: 0;
1533 | opacity: 0;
1534 | }
1535 | #greendoc-search.has-focus .results {
1536 | visibility: visible;
1537 | }
1538 | #greendoc-search.loading .results li.state.loading {
1539 | display: block;
1540 | }
1541 | #greendoc-search.failure .results li.state.failure {
1542 | display: block;
1543 | }
1544 |
1545 | .greendoc-signature {
1546 | margin: 0 0 1em 0;
1547 | padding: 10px;
1548 | border: 1px solid #eee;
1549 | font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
1550 | font-size: 14px;
1551 | overflow-x: auto;
1552 | }
1553 | .greendoc-kind-namespace a {
1554 | text-overflow: ellipsis;
1555 | overflow: hidden;
1556 | }
1557 | .greendoc-signature.greendoc-kind-icon {
1558 | padding-left: 30px;
1559 | }
1560 | .greendoc-signature.greendoc-kind-icon:before {
1561 | top: 10px;
1562 | left: 10px;
1563 | }
1564 | .greendoc-panel > .greendoc-signature {
1565 | margin-left: -20px;
1566 | margin-right: -20px;
1567 | border-width: 1px 0;
1568 | }
1569 | .greendoc-panel > .greendoc-signature.greendoc-kind-icon {
1570 | padding-left: 40px;
1571 | }
1572 | .greendoc-panel > .greendoc-signature.greendoc-kind-icon:before {
1573 | left: 20px;
1574 | }
1575 |
1576 | .greendoc-signature-symbol {
1577 | color: #808080;
1578 | font-weight: normal;
1579 | }
1580 |
1581 | .greendoc-signature-type {
1582 | font-style: italic;
1583 | font-weight: normal;
1584 | }
1585 |
1586 | .greendoc-signatures {
1587 | padding: 0;
1588 | margin: 0 0 1em 0;
1589 | border: 1px solid #eee;
1590 | }
1591 | .greendoc-signatures .greendoc-signature {
1592 | margin: 0;
1593 | border-width: 1px 0 0 0;
1594 | transition: background-color 0.1s;
1595 | }
1596 | .greendoc-signatures .greendoc-signature:first-child {
1597 | border-top-width: 0;
1598 | }
1599 | .greendoc-signatures .greendoc-signature.current {
1600 | background-color: #eee;
1601 | }
1602 | .greendoc-signatures.active > .greendoc-signature {
1603 | cursor: pointer;
1604 | }
1605 | .greendoc-panel > .greendoc-signatures {
1606 | margin-top: -20px;
1607 | margin-left: -20px;
1608 | margin-right: -20px;
1609 | border-width: 1px 0;
1610 | }
1611 | .greendoc-panel > .greendoc-signatures .greendoc-signature.greendoc-kind-icon {
1612 | padding-left: 40px;
1613 | }
1614 | .greendoc-panel > .greendoc-signatures .greendoc-signature.greendoc-kind-icon:before {
1615 | left: 20px;
1616 | }
1617 | .greendoc-panel > a.anchor + .greendoc-signatures {
1618 | border-top-width: 0;
1619 | margin-top: -20px;
1620 | }
1621 |
1622 | ul.greendoc-descriptions {
1623 | position: relative;
1624 | overflow: hidden;
1625 | padding: 0;
1626 | list-style: none;
1627 | }
1628 | ul.greendoc-descriptions.active > .greendoc-description {
1629 | display: none;
1630 | }
1631 | ul.greendoc-descriptions.active > .greendoc-description.current {
1632 | display: block;
1633 | }
1634 | ul.greendoc-descriptions.active > .greendoc-description.fade-in {
1635 | animation: fade-in-delayed 0.3s;
1636 | }
1637 | ul.greendoc-descriptions.active > .greendoc-description.fade-out {
1638 | animation: fade-out-delayed 0.3s;
1639 | position: absolute;
1640 | display: block;
1641 | top: 0;
1642 | left: 0;
1643 | right: 0;
1644 | opacity: 0;
1645 | visibility: hidden;
1646 | }
1647 | ul.greendoc-descriptions h4,
1648 | ul.greendoc-descriptions .greendoc-index-panel h3,
1649 | .greendoc-index-panel ul.greendoc-descriptions h3 {
1650 | font-size: 16px;
1651 | margin: 1em 0 0.5em 0;
1652 | }
1653 |
1654 | ul.greendoc-parameters,
1655 | ul.greendoc-type-parameters {
1656 | list-style: square;
1657 | margin: 0;
1658 | padding-left: 20px;
1659 | }
1660 | ul.greendoc-parameters > li.greendoc-parameter-signature,
1661 | ul.greendoc-type-parameters > li.greendoc-parameter-signature {
1662 | list-style: none;
1663 | margin-left: -20px;
1664 | }
1665 | ul.greendoc-parameters h5,
1666 | ul.greendoc-type-parameters h5 {
1667 | font-size: 16px;
1668 | margin: 1em 0 0.5em 0;
1669 | }
1670 | ul.greendoc-parameters .greendoc-comment,
1671 | ul.greendoc-type-parameters .greendoc-comment {
1672 | margin-top: -0.5em;
1673 | }
1674 |
1675 | .greendoc-sources {
1676 | font-size: 14px;
1677 | color: #808080;
1678 | margin: 0 0 1em 0;
1679 | }
1680 | .greendoc-sources a {
1681 | color: #808080;
1682 | text-decoration: underline;
1683 | }
1684 | .greendoc-sources ul,
1685 | .greendoc-sources p {
1686 | margin: 0 !important;
1687 | }
1688 | .greendoc-sources ul {
1689 | list-style: none;
1690 | padding: 0;
1691 | }
1692 |
1693 | .greendoc-page-toolbar {
1694 | position: fixed;
1695 | z-index: 1;
1696 | top: 0;
1697 | left: 0;
1698 | width: 100%;
1699 | height: 40px;
1700 | color: #333;
1701 | background: #fff;
1702 | border-bottom: 1px solid #eee;
1703 | transition: transform 0.3s linear;
1704 | }
1705 | .greendoc-page-toolbar a {
1706 | color: #333;
1707 | text-decoration: none;
1708 | }
1709 | .greendoc-page-toolbar a.title {
1710 | font-weight: bold;
1711 | }
1712 | .greendoc-page-toolbar a.title:hover {
1713 | text-decoration: underline;
1714 | }
1715 | .greendoc-page-toolbar .table-wrap {
1716 | display: table;
1717 | width: 100%;
1718 | height: 40px;
1719 | }
1720 | .greendoc-page-toolbar .table-cell {
1721 | display: table-cell;
1722 | position: relative;
1723 | white-space: nowrap;
1724 | line-height: 40px;
1725 | }
1726 | .greendoc-page-toolbar .table-cell:first-child {
1727 | width: 100%;
1728 | }
1729 |
1730 | .greendoc-page-toolbar--hide {
1731 | transform: translateY(-100%);
1732 | }
1733 |
1734 | .greendoc-select .greendoc-select-list li:before,
1735 | .greendoc-select .greendoc-select-label:before,
1736 | .greendoc-widget:before {
1737 | content: '';
1738 | display: inline-block;
1739 | width: 40px;
1740 | height: 40px;
1741 | margin: 0 -8px 0 0;
1742 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAoCAQAAAAlSeuiAAABp0lEQVR4Ae3aUa3jQAyF4QNhIBTCQiiEQlgIhRAGhTAQBkIgBEIgDITZZGXNjZTePiSWYqn/54dGfbAq+SiTutWXAgAAAAAAAAAAAAA8NCz1UFSD2lKDS5d3NVzZj/BVNasaLoRZRUmj2lLrVVHWMUntQ13Wj/i1pWa9lprX6xMRnH4dx6Rjsn26+v+12ms+EcB37P0r+qH+DNQGXgMFcHzbregQ78B8eQCTJk0e979ZW7PdA2O49ceDsYexKgUNoI3EKYDWL3D8miaPh/uXtl6BHqEHFQvgXau/FsCiIWAAbST2fpQRT0sl70j3z5ZiBdD7CG5WZX8kxwmgjbiP5GQA9/3O2XaxnnHi53AEE0AbRh+JQwC3/fzC4hcb6xPvS4i3QaMdwX+0utsRPEY6gm2wNhKHAG77eUi7SIcK4G4NY4GMIan2u2Cxqzncl5DUn7Q8ArjvZ8JFOsl/Ed0jyBom+BomQKSto+9PcblHMM4iuu4X0QQw5hrGQY/gUxFkjZuf4m4alXVU+1De/VhEn5CvDSB/RsBzqWgAAAAAAAAAAAAAAACAfyyYJ5nhVuwIAAAAAElFTkSuQmCC);
1743 | background-repeat: no-repeat;
1744 | text-indent: -1024px;
1745 | vertical-align: bottom;
1746 | }
1747 | @media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
1748 | .greendoc-select .greendoc-select-list li:before,
1749 | .greendoc-select .greendoc-select-label:before,
1750 | .greendoc-widget:before {
1751 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAABQCAMAAAC+sjQXAAAAM1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjBUbJAAAAEXRSTlMA3/+/UCBw7xCPYIBAMM+vn1qYQ7QAAALCSURBVHgB7MGBAAAAAICg/akXqQIAAAAAAAAAAAAAAAAAAJids9mdE4bhoDNZCITP93/aSmhV/9uwPWyi8jtkblws2IxsYpz9LwSAaJW8AreE16PxOsMYE6Q4DiYKF7X+8ZHXc/E608xv5snEyIuZrVwMZjbnujR6T3gsXmcLOIRNzD+Ig2UuVtt2+NbAiX/wVLzOlviD9L2BOfGBlL/3D1I+uDjGBJArBPxU3x+K15kCQFo2s21JAOHrKpz4SPrWv4IKA+uFaR6vMwMcb+emA2DWEfDglrkLqEBOKVslA8Dx14oPMiV4CtywWxdQgAwkq2QE0uTXUwJGk2G9s3mTFNBzAkC7HKPsX72AEVjMnAWIpsPCRRjXdQxcjCYpoOcEgHY5Rtk/slWSgM3M2aSeeVgjAOeVpKcdgGMdNAXMuIAqOcZzqF8L+WcAsi8wkTeheCWMegL6mgCorHHyEJ5TVfxrLWDrTUjZdhnhjYqAnlN8TaoELOLVC0gucmoz/3RKcPs2jAs4+J5ET8AEZF+TSgGLeC1V8YuGQQU2IV1Asq9JCwE9XitZVPxr34bpJRj8PqsFLOK108W9aVrWZRrR7Sm2HL4JCToCujHZ6gUs4jUz0P1TEvD+U5wMa363YeziBODIq1YbJrsv9QKW8Ry1nNp+GAHvuingRTfmYcjBf0QpAS37bdUL6PFKtHJq63EsZ5cxcKMkDVIClu1dAK1PcJ5TFQ0M9wZKDCPs3BD7MIJGTs3WfiTfDVQYx5q5ZekCauTU3P5Q0ukGCgh49oFURdobWBY9N/CxEuwGjpGLuPhTdwH1x7HqDDxNgRP2zQ8lraFyF/yJ9vH6QGqtgSbBOU8/j2VORz+Wqfle2d5Ae4R+ML0z7Y+W4P7XHN3AU+tzyK/24EAGAAAAYJC/9T2+CgAAAAAAAAAAAAAAAAAAAADgJpfzHyIKFFBKAAAAAElFTkSuQmCC);
1752 | background-size: 320px 40px;
1753 | }
1754 | }
1755 |
1756 | .greendoc-widget {
1757 | display: inline-block;
1758 | overflow: hidden;
1759 | opacity: 0.6;
1760 | height: 40px;
1761 | transition:
1762 | opacity 0.1s,
1763 | background-color 0.2s;
1764 | vertical-align: bottom;
1765 | cursor: pointer;
1766 | }
1767 | .greendoc-widget:hover {
1768 | opacity: 0.8;
1769 | }
1770 | .greendoc-widget.active {
1771 | opacity: 1;
1772 | background-color: #eee;
1773 | }
1774 | .greendoc-widget.no-caption {
1775 | width: 40px;
1776 | }
1777 | .greendoc-widget.no-caption:before {
1778 | margin: 0;
1779 | }
1780 | .greendoc-widget.search:before {
1781 | background-position: 0 0;
1782 | }
1783 | .greendoc-widget.menu:before {
1784 | background-position: -40px 0;
1785 | }
1786 | .greendoc-widget.options:before {
1787 | background-position: -80px 0;
1788 | }
1789 | .greendoc-widget.options,
1790 | .greendoc-widget.menu {
1791 | display: none;
1792 | background: none;
1793 | outline: none;
1794 | border: none;
1795 | }
1796 | @media (max-width: 900px) {
1797 | .greendoc-widget.options,
1798 | .greendoc-widget.menu {
1799 | display: inline-block;
1800 | }
1801 | }
1802 | input[type='checkbox'] + .greendoc-widget:before {
1803 | background-position: -120px 0;
1804 | }
1805 | input[type='checkbox']:checked + .greendoc-widget:before {
1806 | background-position: -160px 0;
1807 | }
1808 |
1809 | .greendoc-select {
1810 | position: relative;
1811 | display: inline-block;
1812 | height: 40px;
1813 | transition:
1814 | opacity 0.1s,
1815 | background-color 0.2s;
1816 | vertical-align: bottom;
1817 | cursor: pointer;
1818 | }
1819 | .greendoc-select .greendoc-select-label {
1820 | opacity: 0.6;
1821 | transition: opacity 0.2s;
1822 | }
1823 | .greendoc-select .greendoc-select-label:before {
1824 | background-position: -240px 0;
1825 | }
1826 | .greendoc-select.active .greendoc-select-label {
1827 | opacity: 0.8;
1828 | }
1829 | .greendoc-select.active .greendoc-select-list {
1830 | visibility: visible;
1831 | opacity: 1;
1832 | transition-delay: 0s;
1833 | }
1834 | .greendoc-select .greendoc-select-list {
1835 | position: absolute;
1836 | visibility: hidden;
1837 | top: 40px;
1838 | left: 0;
1839 | margin: 0;
1840 | padding: 0;
1841 | opacity: 0;
1842 | list-style: none;
1843 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.25);
1844 | transition:
1845 | visibility 0s 0.2s,
1846 | opacity 0.2s;
1847 | }
1848 | .greendoc-select .greendoc-select-list li {
1849 | padding: 0 20px 0 0;
1850 | background-color: #fdfdfd;
1851 | }
1852 | .greendoc-select .greendoc-select-list li:before {
1853 | background-position: 40px 0;
1854 | }
1855 | .greendoc-select .greendoc-select-list li:nth-child(even) {
1856 | background-color: #fff;
1857 | }
1858 | .greendoc-select .greendoc-select-list li:hover {
1859 | background-color: #eee;
1860 | }
1861 | .greendoc-select .greendoc-select-list li.selected:before {
1862 | background-position: -200px 0;
1863 | }
1864 | @media (max-width: 900px) {
1865 | .greendoc-select .greendoc-select-list {
1866 | top: 0;
1867 | left: auto;
1868 | right: 100%;
1869 | margin-right: -5px;
1870 | }
1871 | .greendoc-select .greendoc-select-label:before {
1872 | background-position: -280px 0;
1873 | }
1874 | }
1875 |
1876 | img {
1877 | max-width: 100%;
1878 | }
1879 |
1880 | /* ==========================================================================
1881 | * * Customizations
1882 | * * ========================================================================== */
1883 |
1884 | body {
1885 | font-family:
1886 | Iowan Old Style,
1887 | Apple Garamond,
1888 | Palatino Linotype,
1889 | Times New Roman,
1890 | 'Droid Serif',
1891 | Times,
1892 | serif;
1893 | }
1894 |
1895 | .header-badge img {
1896 | vertical-align: sub;
1897 | margin-left: 0.5em;
1898 | }
1899 |
1900 | .greendoc-typography {
1901 | line-height: 1.6em;
1902 | }
1903 |
1904 | .greendoc-navigation h4 {
1905 | margin-bottom: 0.5em;
1906 | }
1907 |
1908 | .greendoc-navigation.secondary ul li a {
1909 | padding-left: 1em;
1910 | }
1911 |
1912 | .greendoc-navigation.secondary ul li a.active {
1913 | text-decoration: underline;
1914 | }
1915 |
1916 | .greendoc-panel {
1917 | box-shadow: none;
1918 | }
1919 |
1920 | pre {
1921 | padding: 20px;
1922 | white-space: pre;
1923 | overflow: auto;
1924 | }
1925 |
1926 | .greendoc-signature {
1927 | color: #4da6ff;
1928 | }
1929 |
1930 | html.minimal .greendoc-generator,
1931 | .greendoc-panel > .greendoc-signature.greendoc-kind-icon,
1932 | .greendoc-panel > .greendoc-signatures .greendoc-signature.greendoc-kind-icon {
1933 | padding-left: 20px;
1934 | }
1935 |
1936 | .greendoc-select .greendoc-select-label,
1937 | .greendoc-widget {
1938 | opacity: 0.8;
1939 | }
1940 |
1941 | .greendoc-select.active .greendoc-select-label,
1942 | .greendoc-widget:hover {
1943 | opacity: 1;
1944 | }
1945 |
1946 | .greendoc-navigation a[target='_blank']::after {
1947 | content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
1948 | margin: 0 3px 0 5px;
1949 | }
1950 |
1951 | /** Enum styles are too sprawling. */
1952 | .greendoc-parent-kind-object-literal {
1953 | padding-bottom: 0;
1954 | margin-bottom: 0;
1955 | margin-top: 0;
1956 | padding-top: 0;
1957 | }
1958 |
1959 | .greendoc-parent-kind-object-literal .greendoc-sources {
1960 | display: none;
1961 | }
1962 |
1963 | .greendoc-parent-kind-object-literal .greendoc-signature.greendoc-kind-icon {
1964 | border-bottom: none;
1965 | padding-bottom: 0;
1966 | }
1967 |
1968 | .greendoc-parent-kind-object-literal .greendoc-comment.greendoc-typography p {
1969 | margin-bottom: 0.3em;
1970 | }
1971 |
1972 | blockquote {
1973 | border-left: 6px solid #ccc;
1974 | padding-left: 1em;
1975 | margin-left: 0;
1976 | color: #606060;
1977 | font-style: italic;
1978 | }
1979 |
1980 | html.minimal .greendoc-generator {
1981 | text-align: center;
1982 | padding-left: 0;
1983 | }
1984 |
1985 | .greendoc-generator img {
1986 | max-width: 40%;
1987 | }
1988 |
1989 | details[open] {
1990 | padding-left: 1em;
1991 | margin-bottom: 1em;
1992 | }
1993 |
1994 | details[open] > summary {
1995 | margin-left: -1em;
1996 | }
1997 |
1998 | details > summary {
1999 | cursor: pointer;
2000 | }
2001 |
2002 | @media (max-width: 900px) {
2003 | html.minimal .greendoc-navigation {
2004 | display: block;
2005 | top: 0;
2006 | left: -305px; /* Additional 5px for box-shadow blur. */
2007 | max-height: none;
2008 | padding-top: 40px;
2009 | background: #fff;
2010 | box-shadow: 1px 1px 3px 0 rgba(0, 0, 0, 0.16);
2011 | transition: left 0.2s ease;
2012 | }
2013 |
2014 | html.minimal .toggle-nav .greendoc-navigation {
2015 | left: 0;
2016 | }
2017 |
2018 | .greendoc-generator img {
2019 | max-width: 60%;
2020 | }
2021 |
2022 | .greendoc-generator .line {
2023 | display: block;
2024 | line-height: 2em;
2025 | }
2026 |
2027 | .greendoc-generator .divider {
2028 | display: none;
2029 | }
2030 | }
2031 |
--------------------------------------------------------------------------------