├── .editorconfig ├── .github └── workflows │ ├── codeql-analysis.yml │ └── main.yml ├── .gitignore ├── .node-version ├── .prettierrc.json ├── .vim-lsp-settings └── settings.json ├── LICENSE ├── README.md ├── docker-compose.yml ├── docker ├── app │ └── Dockerfile └── nginx │ └── Dockerfile ├── nginx └── nginx.conf ├── package.json ├── packages ├── ichigo │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── pipe.spec.ts │ │ ├── html │ │ │ ├── __tests__ │ │ │ │ └── classnames.spec.ts │ │ │ ├── classnames.ts │ │ │ └── index.ts │ │ ├── http │ │ │ ├── __tests__ │ │ │ │ └── content-type.spec.ts │ │ │ ├── content-type.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ └── pipe.ts │ ├── tsconfig.esm.json │ └── tsconfig.json ├── knife │ ├── README.md │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── tokenizer.spec.ts │ │ └── tokenizer.ts │ ├── tsconfig.esm.json │ ├── tsconfig.json │ └── yarn.lock ├── lemon │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── css.spec.ts │ │ │ ├── index.spec.ts │ │ │ ├── jsx.spec.tsx │ │ │ └── renderToString.spec.tsx │ │ ├── autocomplete.ts │ │ ├── createElement.ts │ │ ├── css.ts │ │ ├── index.ts │ │ ├── jsx.ts │ │ ├── renderToString.ts │ │ ├── rfc2978.ts │ │ ├── role.ts │ │ └── wai-aria.ts │ ├── tsconfig.esm.json │ └── tsconfig.json ├── melon │ ├── .gitignore │ ├── README.md │ ├── for-local │ │ └── nginx.templ.conf │ ├── log │ │ └── .keep │ ├── package.json │ ├── src │ │ ├── assets │ │ │ ├── css │ │ │ │ ├── markdown.css │ │ │ │ ├── reset.css │ │ │ │ └── theme.css │ │ │ ├── favicon.ico │ │ │ ├── icons │ │ │ │ ├── icon-128x128.png │ │ │ │ ├── icon-144x144.png │ │ │ │ ├── icon-152x152.png │ │ │ │ ├── icon-192x192.png │ │ │ │ ├── icon-384x384.png │ │ │ │ ├── icon-512x512.png │ │ │ │ ├── icon-72x72.png │ │ │ │ └── icon-96x96.png │ │ │ ├── images │ │ │ │ ├── loading.svg │ │ │ │ ├── ogp-pipeline-operator.png │ │ │ │ └── states-of-js-2020.png │ │ │ ├── manifest.json │ │ │ ├── register.js │ │ │ ├── robots.txt │ │ │ └── sw.js │ │ ├── components │ │ │ ├── organisms │ │ │ │ └── nav │ │ │ │ │ └── index.tsx │ │ │ └── templates │ │ │ │ ├── html.tsx │ │ │ │ └── page.tsx │ │ ├── index.tsx │ │ └── pages │ │ │ ├── blog │ │ │ ├── 20201221 │ │ │ │ └── index.tsx │ │ │ ├── 20211204 │ │ │ │ └── index.tsx │ │ │ ├── articles.tsx │ │ │ └── index.tsx │ │ │ └── index.tsx │ └── tsconfig.json ├── naruto │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── req.ts │ │ └── res.ts │ ├── tsconfig.esm.json │ └── tsconfig.json ├── suica │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── index.spec.ts │ │ ├── index.ts │ │ └── middleware │ │ │ ├── body-parser │ │ │ ├── index.ts │ │ │ ├── json.ts │ │ │ └── utils.ts │ │ │ └── index.ts │ ├── tsconfig.esm.json │ └── tsconfig.json └── zakuro │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ ├── atoms │ │ ├── bold │ │ │ ├── index.tsx │ │ │ └── story.tsx │ │ ├── index.tsx │ │ └── time │ │ │ ├── index.tsx │ │ │ └── story.tsx │ ├── explorer │ │ ├── assets │ │ │ ├── index.css │ │ │ └── index.js │ │ ├── import.tsx │ │ ├── index.tsx │ │ └── root.tsx │ └── index.tsx │ ├── tsconfig.esm.json │ └── tsconfig.json ├── renovate.json ├── tsconfig.json ├── tsconfig.node.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | 8 | [{*.json,*.yml,*.ts,*.tsx,*.js}] 9 | indent_style = space 10 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '15 8 * * 2' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | node-version: [ 14, 16, 18 ] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v2 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | 21 | - name: Cache node modules 22 | uses: actions/cache@v3 23 | with: 24 | # npm cache files are stored in `~/.npm` on Linux/macOS 25 | path: '**/node_modules' 26 | key: ${{ runner.os }}-modules--${{ hashFiles('**/yarn.lock') }}--${{ hashFiles('**/package.json') }} 27 | 28 | - run: npm config set scripts-prepend-node-path true 29 | 30 | - run: npm install -g yarn 31 | 32 | - run: yarn install 33 | 34 | - run: yarn build 35 | env: 36 | NODE_ENV: production 37 | 38 | - run: yarn test 39 | env: 40 | NODE_ENV: test 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | esm 3 | node_modules 4 | *.tsbuildinfo 5 | *.log 6 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 16.15.0 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "singleQuote": false, 5 | "trailingComma": "all", 6 | "semi": true, 7 | "arrowParens": "always", 8 | "parser": "typescript" 9 | } 10 | -------------------------------------------------------------------------------- /.vim-lsp-settings/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "efm-langserver": { 3 | "disabled": false 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [2020] [maxmellon] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :tropical_drink: kajitsu - 果実 2 | 3 | [![Actions Status: CI](https://github.com/MaxMEllon/kajitsu/workflows/CI/badge.svg)](https://github.com/MaxMEllon/kajitsu/actions?query=workflow%3A"CI") 4 | 5 | ## LICENSE 6 | 7 | MIT 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | app: 5 | build: 6 | context: . 7 | dockerfile: ./docker/app/Dockerfile 8 | ports: 9 | - "3000:3000" 10 | 11 | nginx: 12 | build: 13 | context: . 14 | dockerfile: ./docker/nginx/Dockerfile 15 | depends_on: 16 | - app 17 | ports: 18 | - "8080:80" 19 | -------------------------------------------------------------------------------- /docker/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-slim as node 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y locales tzdata busybox \ 5 | && apt-get clean && rm -rf /var/lib/apt/lists/* \ 6 | && echo "# Install Busybox" \ 7 | && mkdir /usr/local/busybox \ 8 | && busybox --install /usr/local/busybox \ 9 | && echo "# Generate locale" \ 10 | && echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen \ 11 | && echo "ja_JP.UTF-8 UTF-8" >> /etc/locale.gen \ 12 | && locale-gen 13 | ENV PATH "$PATH:/usr/local/busybox" 14 | ENV TZ "Asia/Tokyo" 15 | ENV LANG "ja_JP.UTF-8" 16 | ENV LC_ALL "ja_JP.UTF-8" 17 | 18 | 19 | FROM node AS build 20 | WORKDIR /sources 21 | COPY yarn.lock package.json tsconfig.json tsconfig.node.json ./ 22 | COPY . ./ 23 | RUN yarn && yarn build 24 | 25 | FROM node AS app 26 | 27 | WORKDIR /app 28 | COPY --from=build /sources/node_modules/ /app/node_modules 29 | COPY --from=build /sources/packages/ /app/packages 30 | 31 | WORKDIR /app/packages/melon 32 | CMD yarn run -s production 33 | -------------------------------------------------------------------------------- /docker/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.23.0-alpine 2 | 3 | RUN apk --no-cache add tzdata && \ 4 | cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \ 5 | apk del tzdata 6 | 7 | COPY ./nginx/nginx.conf /etc/nginx/nginx.conf 8 | 9 | CMD sh -c "exec nginx -g 'daemon off;'" 10 | 11 | 12 | -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes auto; 3 | 4 | error_log /var/log/nginx/error.log warn; 5 | pid /var/run/nginx.pid; 6 | 7 | events { 8 | worker_connections 1024; 9 | } 10 | 11 | http { 12 | include /etc/nginx/mime.types; 13 | default_type application/octet-stream; 14 | server_tokens off; 15 | 16 | log_format main 'time:$time_iso8601\t' 17 | 'protocol:$server_protocol\t' 18 | 'method:$request_method\t' 19 | 'path:$request_uri\t' 20 | 'status:$status\t' 21 | 'request_time:$request_time\t' 22 | 'upstream_response_time:$upstream_response_time\t' 23 | 'body_bytes_sent:$body_bytes_sent\t' 24 | 'remote_addr:$remote_addr\t' 25 | 'x_forwarded_for:$http_x_forwarded_for\t' 26 | 'referer:$http_referer\t' 27 | 'user_agent:$http_user_agent\t'; 28 | 29 | access_log /var/log/nginx/access.log main; 30 | 31 | proxy_cache_path /var/cache/nginx/app levels=1:2 keys_zone=app:50m inactive=1d use_temp_path=off; 32 | 33 | upstream app { 34 | server app:3000; 35 | keepalive 32; 36 | } 37 | 38 | server { 39 | listen 80; 40 | keepalive_timeout 5; 41 | 42 | proxy_connect_timeout 5; 43 | proxy_send_timeout 30; 44 | proxy_read_timeout 30; 45 | proxy_intercept_errors on; 46 | proxy_hide_header X-Powered-By; 47 | 48 | location / { 49 | proxy_cache app; 50 | proxy_cache_valid 120m; 51 | 52 | add_header Permissions-Policy "sync-xhr 'none'"; 53 | add_header X-Frame-Options "SAMEORIGIN"; 54 | add_header X-XSS-Protection "1; mode=block"; 55 | add_header X-Content-Type-Options "nosniff"; 56 | add_header Strict-Transport-Security "max-age=31536000"; 57 | 58 | proxy_pass http://app; 59 | } 60 | 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kajitsu", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "yarn workspaces run build", 8 | "test": "yarn workspaces run test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+ssh://git@github.com/MaxMEllon/kajitsu.git" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/MaxMEllon/kajitsu/issues" 18 | }, 19 | "homepage": "https://github.com/MaxMEllon/kajitsu#readme", 20 | "private": true, 21 | "workspaces": [ 22 | "packages/naruto", 23 | "packages/ichigo", 24 | "packages/knife", 25 | "packages/lemon", 26 | "packages/suica", 27 | "packages/zakuro", 28 | "packages/melon" 29 | ], 30 | "devDependencies": { 31 | "@types/jest": "26.0.24", 32 | "@types/node": "16.11.35", 33 | "jest": "26.6.3", 34 | "ts-jest": "26.5.6", 35 | "typescript": "4.6.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/ichigo/README.md: -------------------------------------------------------------------------------- 1 | # :strawberry: kajitsu:ichigo - 果実:いちご 2 | > つぶつぶイチゴの関数ユーティリティ 3 | 4 | ## LICENSE 5 | 6 | MIT 7 | -------------------------------------------------------------------------------- /packages/ichigo/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ["."], 3 | transform: { 4 | "^.+\\.tsx?$": "ts-jest", 5 | }, 6 | testRegex: "/__tests__/.*\\.spec\\.tsx?$", 7 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/ichigo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kajitsu/ichigo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "build/index.js", 6 | "module": "esm/index.js", 7 | "types": "esm/index.d.ts", 8 | "scripts": { 9 | "build:cjs": "tsc -d -p ./tsconfig.json", 10 | "build:esm": "tsc -d -p ./tsconfig.esm.json", 11 | "build": "npm run build:cjs && npm run build:esm", 12 | "test": "jest" 13 | }, 14 | "dependencies": { 15 | "@kajitsu/naruto": "1.0.0" 16 | }, 17 | "author": "", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /packages/ichigo/src/__tests__/pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | import { pipe } from "../pipe"; 3 | 4 | describe("pipe", () => { 5 | it("1op", () => { 6 | const op1 = (val: number) => val + 1; 7 | const actual = pipe(1, op1); 8 | assert.strictEqual(actual, 2); 9 | }); 10 | 11 | it("2op", () => { 12 | const op1 = (val: number) => val + 1; 13 | const op2 = (val: number): string => String(val); 14 | const actual = pipe(1, op1, op2); 15 | assert.strictEqual(actual, "2"); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/ichigo/src/html/__tests__/classnames.spec.ts: -------------------------------------------------------------------------------- 1 | import { classnames } from "../classnames"; 2 | 3 | describe("classnames", () => { 4 | it.each` 5 | args | expected 6 | ${["a", "b", false && "c"]} | ${"a b"} 7 | ${[true ? "a" : "b", "c", "b"]} | ${"a c b"} 8 | `(`returns $expected when got $args`, ({ args, expected }) => { 9 | const actual = classnames(...args); 10 | expect(actual).toBe(expected); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/ichigo/src/html/classnames.ts: -------------------------------------------------------------------------------- 1 | export const classnames = ( 2 | ...names: Array 3 | ): string => { 4 | return names.filter((x) => x).join(" "); 5 | }; 6 | -------------------------------------------------------------------------------- /packages/ichigo/src/html/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./classnames"; 2 | -------------------------------------------------------------------------------- /packages/ichigo/src/http/__tests__/content-type.spec.ts: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | import { IncomingMessage } from "http" 3 | import { createMockIncomingMessage } from "@kajitsu/naruto"; 4 | import { contentType, MediaType } from "../content-type"; 5 | 6 | describe("content-type", () => { 7 | it("got media object when give valid `content-type` string", () => { 8 | const req = createMockIncomingMessage({ 9 | headers: { "content-type": "application/json" }, 10 | }); 11 | const actual = contentType(req); 12 | assert.strictEqual(actual.type, "application"); 13 | assert.strictEqual(actual.subtype, "json"); 14 | }); 15 | 16 | const table: Array<[ 17 | string, 18 | { 19 | given: IncomingMessage 20 | expected: MediaType 21 | } 22 | ]> = [ 23 | [ 24 | 'application/vnd.ms-excel', 25 | { 26 | given: createMockIncomingMessage({ 27 | headers: { 'content-type': 'application/vnd.ms-excel' } 28 | }), 29 | expected: { 30 | type: 'application', 31 | subtype: 'vnd.ms-excel', 32 | suffix: undefined 33 | } 34 | }, 35 | ], 36 | [ 37 | 'application/svg+xml', 38 | { 39 | given: createMockIncomingMessage({ 40 | headers: { 'content-type': 'image/svg+xml' } 41 | }), 42 | expected: { 43 | type: 'image', 44 | subtype: 'svg', 45 | suffix: 'xml' 46 | } 47 | }, 48 | ] 49 | ] 50 | 51 | it.each(table)('got %s', (name, { given, expected }) => { 52 | const actual = contentType(given) 53 | assert.deepStrictEqual(actual, expected) 54 | }) 55 | }); 56 | -------------------------------------------------------------------------------- /packages/ichigo/src/http/content-type.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage } from "http"; 2 | 3 | const TYPE_REGEXP = /^ *([A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126})\/([A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}) *$/; 4 | 5 | export type MediaType = { 6 | type: string; 7 | subtype: string; 8 | suffix?: string; 9 | }; 10 | 11 | const parse = (contentType: string | void): MediaType => { 12 | if (!contentType || typeof contentType !== "string") { 13 | throw new TypeError(`expected string, but got ${typeof contentType}`); 14 | } 15 | 16 | const match = TYPE_REGEXP.exec(contentType.toLowerCase()); 17 | if (!match) { 18 | throw new TypeError("invalid format"); 19 | } 20 | 21 | const type = match[1]; 22 | let subtype = match[2]; 23 | let suffix; 24 | 25 | const index = subtype.lastIndexOf("+"); 26 | 27 | if (index !== -1) { 28 | suffix = subtype.substring(index + 1); 29 | subtype = subtype.substring(0, index); 30 | } 31 | 32 | return { 33 | type, 34 | subtype, 35 | suffix, 36 | }; 37 | }; 38 | 39 | export const contentType = (req: IncomingMessage): MediaType => { 40 | const contentType = req.headers["content-type"]; 41 | const mediaType = parse(contentType); 42 | return mediaType; 43 | }; 44 | -------------------------------------------------------------------------------- /packages/ichigo/src/http/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./content-type"; 2 | -------------------------------------------------------------------------------- /packages/ichigo/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./pipe"; 2 | export * from "./html"; 3 | export * as http from "./http"; 4 | -------------------------------------------------------------------------------- /packages/ichigo/src/pipe.ts: -------------------------------------------------------------------------------- 1 | export function pipe(value: A, op1: (input: A) => B): B; 2 | export function pipe( 3 | value: A, 4 | op1: (input: A) => B, 5 | op2: (input: B) => C 6 | ): C; 7 | 8 | export function pipe( 9 | value: A, 10 | op1: (input: A) => B, 11 | op2: (input: B) => C, 12 | op3: (input: C) => D 13 | ): D; 14 | 15 | export function pipe( 16 | value: A, 17 | op1: (input: A) => B, 18 | op2: (input: B) => C, 19 | op3: (input: C) => D, 20 | op4: (input: D) => E 21 | ): E; 22 | 23 | export function pipe( 24 | value: A, 25 | op1: (input: A) => B, 26 | op2: (input: B) => C, 27 | op3: (input: C) => D, 28 | op4: (input: D) => E, 29 | op5: (input: E) => F 30 | ): F; 31 | 32 | export function pipe( 33 | value: any, 34 | ...operations: Array<(value: any) => any> 35 | ): any { 36 | return operations.reduce((acc, cur) => { 37 | return cur(acc); 38 | }, value); 39 | } 40 | -------------------------------------------------------------------------------- /packages/ichigo/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "rootDir": "./src", 6 | "outDir": "./esm" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/ichigo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "rootDir": "./src", 6 | "outDir": "./build", 7 | "esModuleInterop": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/knife/README.md: -------------------------------------------------------------------------------- 1 | # :hocho: kajitsu:knife - 果実:ナイフ 2 | > 果物ナイフでマークダウン 3 | 4 | ## LICENSE 5 | 6 | MIT 7 | -------------------------------------------------------------------------------- /packages/knife/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ["."], 3 | transform: { 4 | "^.+\\.tsx?$": "ts-jest", 5 | }, 6 | testRegex: "/__tests__/.*\\.spec\\.tsx?$", 7 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/knife/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kajitsu/knife", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1 5 | } 6 | -------------------------------------------------------------------------------- /packages/knife/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kajitsu/knife", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "build/index.js", 6 | "module": "esm/index.js", 7 | "types": "esm/index.d.ts", 8 | "scripts": { 9 | "build:cjs": "tsc -d -p ./tsconfig.json", 10 | "build:esm": "tsc -d -p ./tsconfig.esm.json", 11 | "build": "npm run build:cjs && npm run build:esm", 12 | "test": "jest" 13 | }, 14 | "dependencies": {}, 15 | "author": "", 16 | "license": "MIT" 17 | } 18 | -------------------------------------------------------------------------------- /packages/knife/src/__tests__/tokenizer.spec.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import { Tokenizer, Token } from '../tokenizer' 3 | 4 | describe("tokenizer", () => { 5 | it.skip("works", () => { 6 | const text = `# hoge` 7 | const tokenizer = new Tokenizer(text) 8 | const iterator = tokenizer.tokenize() 9 | let result: Array = [] 10 | let itr: IteratorResult = iterator.next() 11 | while (!itr.done) { 12 | console.log(itr.value) 13 | result.push(itr.value) 14 | itr = iterator.next() 15 | } 16 | assert.deepStrictEqual(result, [ 17 | { 18 | type: 'SHARP', 19 | literal: '#', 20 | }, 21 | { 22 | type: 'PLAIN', 23 | literal: 'hoge', 24 | }, 25 | { 26 | type: 'EOS', 27 | literal: 'EOS' 28 | } 29 | ]) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /packages/knife/src/tokenizer.ts: -------------------------------------------------------------------------------- 1 | const tokens = new Map([ 2 | ['[', 'L_SQUARE'], 3 | [']', 'R_SQUARE'], 4 | ['`', 'BACKQUOTE'], 5 | ['*', 'ASTAH'], 6 | ['(', 'L_BRACE'], 7 | [')', 'R_BRACE'], 8 | ['#', 'SHARP'], 9 | [';', 'SEMICORON'] 10 | ]) 11 | 12 | const EOS = '#-- EOS --#' 13 | 14 | const whitespaces = new Set([ 15 | ' ', 16 | '\n', 17 | '\r', 18 | '\t' 19 | ]) 20 | 21 | export type Token = { 22 | type: string 23 | literal: string 24 | } 25 | 26 | export class Tokenizer { 27 | private rawText: string 28 | // walk by code point 29 | private readPosition: number = 0; 30 | // current code point 31 | private char: string 32 | private isEnd: boolean = false 33 | 34 | constructor(input: string) { 35 | this.rawText = input 36 | this.char = String.fromCodePoint(0) 37 | } 38 | 39 | *tokenize(): Generator { 40 | while (!this.isEnd) { 41 | console.log(this.readPosition) 42 | this.read() 43 | this.skipWhiteSpace() 44 | const maybeToken = tokens.get(this.char) 45 | let token: Token 46 | if (this.char === EOS) { 47 | token = { 48 | type: 'EOS', 49 | literal: 'EOS', 50 | } 51 | } else if (typeof maybeToken === 'undefined') { 52 | this.readPlainText() 53 | token = { 54 | type: 'PLAIN', 55 | literal: this.char.toString() 56 | } 57 | } else { 58 | token = { 59 | type: maybeToken, 60 | literal: this.char.toString() 61 | } 62 | } 63 | yield token 64 | } 65 | return true 66 | } 67 | 68 | skipWhiteSpace() { 69 | while (whitespaces.has) { 70 | this.read() 71 | } 72 | } 73 | 74 | readPlainText() { 75 | while (!tokens.has(this.char) || !whitespaces.has(this.char)) { 76 | const codePoint = this.rawText.codePointAt(this.readPosition) 77 | if (!codePoint) return 78 | this.char += String.fromCodePoint(codePoint) 79 | this.readPosition++ 80 | } 81 | } 82 | 83 | read() { 84 | const codePoint = this.rawText.codePointAt(this.readPosition) 85 | // is end of text if code point is undefined. 86 | if (typeof codePoint === 'undefined') { 87 | this.char = EOS 88 | this.isEnd = true 89 | } else { 90 | this.char = String.fromCodePoint(codePoint) 91 | } 92 | this.readPosition++ 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /packages/knife/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "rootDir": "./src", 6 | "outDir": "./esm" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/knife/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "rootDir": "./src", 6 | "outDir": "./build", 7 | "jsx": "react", 8 | "jsxFactory": "h" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/knife/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/lemon/README.md: -------------------------------------------------------------------------------- 1 | # :lemon: kajitsu:lemon - 果実:れもん 2 | > jsx 3 | 4 | ## LICENSE 5 | 6 | MIT 7 | -------------------------------------------------------------------------------- /packages/lemon/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ["."], 3 | transform: { 4 | "^.+\\.tsx?$": "ts-jest", 5 | }, 6 | testRegex: "/__tests__/.*\\.spec\\.tsx?$", 7 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/lemon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kajitsu/lemon", 3 | "version": "1.0.0", 4 | "main": "build/index.js", 5 | "module": "esm/index.js", 6 | "types": "esm/index.d.ts", 7 | "scripts": { 8 | "build:cjs": "tsc -d -p ./tsconfig.json", 9 | "build:esm": "tsc -d -p ./tsconfig.esm.json", 10 | "prebuild": "rm -rf esm build *.tsbuildinfo", 11 | "build": "npm run build:esm && npm run build:cjs", 12 | "test": "jest" 13 | }, 14 | "dependencies": { 15 | "@kajitsu/ichigo": "1.0.0" 16 | }, 17 | "author": "", 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /packages/lemon/src/__tests__/css.spec.ts: -------------------------------------------------------------------------------- 1 | import { createStyleContext, renderToString } from ".."; 2 | import { css, renderToStyleString } from "../css"; 3 | 4 | describe("css", () => { 5 | it("works", () => { 6 | const Component = css("div")` 7 | color: black; 8 | font-weight: 800; 9 | `; 10 | const ctx = createStyleContext().set(); 11 | const string = renderToString(Component({})); 12 | const act = renderToStyleString(ctx); 13 | const classNames = ctx.getClassNames(); 14 | expect(string).toEqual(`
`); 15 | expect(act).toEqual(`.${classNames[0]} { color: black; font-weight: 800; }`); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/lemon/src/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | import { h } from ".."; 3 | 4 | describe("createElement", () => { 5 | it("expected to get type as string VNode when give 1st args string", () => { 6 | const actual = h("p", { id: "hoge" }); 7 | assert.deepStrictEqual(actual, { 8 | props: { 9 | children: [], 10 | id: "hoge", 11 | }, 12 | type: "p", 13 | }); 14 | }); 15 | 16 | it("expected to get nested VNode when nested call", () => { 17 | const actual = h("div", {}, h("div", {}), h("div", {})); 18 | assert.deepStrictEqual(actual, { 19 | props: { 20 | children: [ 21 | { 22 | props: { 23 | children: [], 24 | }, 25 | type: "div", 26 | }, 27 | { 28 | props: { 29 | children: [], 30 | }, 31 | type: "div", 32 | }, 33 | ], 34 | }, 35 | type: "div", 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/lemon/src/__tests__/jsx.spec.tsx: -------------------------------------------------------------------------------- 1 | import { h, FC } from ".."; 2 | import { renderToString } from "../renderToString"; 3 | 4 | describe("renderToString", () => { 5 | it("works", () => { 6 | const actual = renderToString( 7 | 8 | 9 | 10 | 11 | ); 12 | expect(actual).toEqual(""); 13 | }); 14 | 15 | it("works with FC", () => { 16 | const Html: FC = () => ( 17 | 18 | 19 | 20 | 21 | ); 22 | const actual = renderToString(); 23 | expect(actual).toEqual(""); 24 | }); 25 | 26 | it("works with children", () => { 27 | const Html: FC = ({ children }) => ( 28 | 29 | 30 | {children} 31 | 32 | ); 33 | const P: FC = ({ children }) =>

{children}

; 34 | const actual = renderToString( 35 | 36 |

hoge

37 | 38 | ); 39 | expect(actual).toEqual( 40 | "

hoge

" 41 | ); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/lemon/src/__tests__/renderToString.spec.tsx: -------------------------------------------------------------------------------- 1 | import { h, FC, Fragment } from ".."; 2 | import { renderToString } from "../renderToString"; 3 | 4 | describe("renderToString", () => { 5 | it("works", () => { 6 | const actual = renderToString( 7 | // prettier-ignore 8 | h('div', {}, 9 | h('ul', {}, 10 | h('li', {}, 'foo'), 11 | h('li', {}, 'bar'), 12 | h('li', {}, 'hoge') 13 | ) 14 | ) 15 | ); 16 | expect(actual).toEqual( 17 | "
  • foo
  • bar
  • hoge
" 18 | ); 19 | }); 20 | 21 | it("expect to set class if got className", () => { 22 | const actual = renderToString( 23 | // prettier-ignore 24 | h('div', { className: 'hoge' }) 25 | ); 26 | expect(actual).toEqual(`
`); 27 | }); 28 | 29 | it("expect joined string if got aria-relevant token list", () => { 30 | const actual = renderToString(
); 31 | expect(actual).toEqual(`
`); 32 | }); 33 | 34 | it("async/defer attribute expected key only", () => { 35 | const actual1 = renderToString(`); 37 | const actual2 = renderToString(`); 39 | }); 40 | 41 | it("Fragment works", () => { 42 | const X: FC = () =>
x
43 | const actual = renderToString(<>

a

b

); 44 | expect(actual).toEqual('

a

x

b

'); 45 | }) 46 | }); 47 | -------------------------------------------------------------------------------- /packages/lemon/src/autocomplete.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute 3 | */ 4 | export type AutoComplete = 5 | | "name" 6 | | "honorific-prefix" 7 | | "given-name" 8 | | "additional-name" 9 | | "family-name" 10 | | "honorific-suffix" 11 | | "nickname" 12 | | "username" 13 | | "new-password" 14 | | "current-password" 15 | | "one-time-code" 16 | | "organization-title" 17 | | "organization" 18 | | "street-address" 19 | | "address-line1" 20 | | "address-line2" 21 | | "address-line3" 22 | | "address-level4" 23 | | "address-level3" 24 | | "address-level2" 25 | | "address-level1" 26 | | "country" 27 | | "country-name" 28 | | "postal-code" 29 | | "cc-name" 30 | | "cc-given-name" 31 | | "cc-additional-name" 32 | | "cc-family-name" 33 | | "cc-number" 34 | | "cc-exp" 35 | | "cc-exp-month" 36 | | "cc-exp-year" 37 | | "cc-csc" 38 | | "cc-type" 39 | | "transaction-currency" 40 | | "transaction-amount" 41 | | "language" 42 | | "bday" 43 | | "bday-day" 44 | | "bday-month" 45 | | "bday-year" 46 | | "sex" 47 | | "url" 48 | | "photo"; 49 | -------------------------------------------------------------------------------- /packages/lemon/src/createElement.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GlobalAttributes, 3 | VNode, 4 | FC, 5 | Children, 6 | ComponentChildren, 7 | InternalIntrinsicElements, 8 | } from "./jsx"; 9 | 10 | namespace JSXInternal { 11 | export type Element = VNode; 12 | export type IntrinsicElements = InternalIntrinsicElements; 13 | } 14 | 15 | export type HTMLAttributes = GlobalAttributes; 16 | 17 | export function createElement( 18 | type: string, 19 | props: HTMLAttributes | null, 20 | ...children: ComponentChildren[] 21 | ): VNode; 22 | 23 | export function createElement

( 24 | type: FC

, 25 | props: (P & { children?: ComponentChildren }) | null, 26 | ...children: ComponentChildren[] 27 | ): VNode; 28 | 29 | export function createElement(type: any, props: any, ...children: any) { 30 | return { 31 | type, 32 | props: { 33 | children, 34 | ...props, 35 | }, 36 | }; 37 | } 38 | export namespace createElement { 39 | export import JSX = JSXInternal; 40 | } 41 | 42 | export function h( 43 | type: string, 44 | props: (HTMLAttributes & { className?: string }) | null, 45 | ...children: ComponentChildren[] 46 | ): VNode; 47 | 48 | export function h

( 49 | type: FC

, 50 | props: (P & { className?: string; children?: ComponentChildren }) | null, 51 | ...children: Children[] 52 | ): VNode; 53 | 54 | export function h(type: any, props: any, ...children: any) { 55 | // FXIME: should not use `.flat()` 56 | return createElement(type, props, ...children.flat()); 57 | } 58 | 59 | export const Fragment: FC = ({ children }) => { 60 | if (children) { 61 | return children as h.JSX.Element 62 | } 63 | return null 64 | } 65 | 66 | export namespace h { 67 | export import JSX = JSXInternal; 68 | } 69 | -------------------------------------------------------------------------------- /packages/lemon/src/css.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@kajitsu/ichigo"; 2 | import { FC, h } from "."; 3 | import { InternalIntrinsicElements } from "./jsx"; 4 | 5 | const randomString = (): string => { 6 | const str = 7 | 'le-' + 8 | Math.random().toString(36).substring(2, 5) + 9 | Math.random().toString(36).substring(2, 5); 10 | for (const ctx of allContext) { 11 | if (ctx.map.has(str)) return randomString(); 12 | } 13 | return str; 14 | }; 15 | 16 | export interface Context { 17 | map: Map, 18 | getClassNames(): string[], 19 | set(): this 20 | remove(): void 21 | } 22 | 23 | let currentContext: Context | undefined; 24 | const allContext: Context[] = []; 25 | 26 | 27 | export const createStyleContext = () => { 28 | const ctx: Context = { 29 | map: new Map(), 30 | getClassNames(): string[] { 31 | return Array.from(this.map.keys()); 32 | }, 33 | set() { 34 | currentContext = this; 35 | allContext.push(ctx); 36 | return this; 37 | }, 38 | remove() { 39 | const idx = allContext.findIndex((ctx) => ctx === this); 40 | if (idx === -1) return; 41 | allContext.splice(idx, 1); 42 | }, 43 | }; 44 | return ctx; 45 | }; 46 | 47 | export const refreshContext = () => (currentContext = void 0); 48 | 49 | const trimSpace = (str: string): string => str.replace(/\s+/g, " "); 50 | const trimNewLine = (str: string): string => str.replace("\n", ""); 51 | const trim = (str: string) => pipe(str, trimSpace, trimNewLine); 52 | 53 | /** 54 | * @summary should be call renderToString before this. 55 | */ 56 | export const renderToStyleString = (ctx: Context) => ( 57 | Array 58 | .from(ctx.map.entries()) 59 | .map( 60 | ([key, val]) => `.${key} {${trim(val) 61 | .split(";") 62 | .filter((x) => x) 63 | .join(";")}}` 64 | ) 65 | .join("") 66 | ) 67 | 68 | type PropTypes = T extends FC< 69 | infer P 70 | > 71 | ? P 72 | : T extends keyof InternalIntrinsicElements 73 | ? InternalIntrinsicElements[T] 74 | : unknown; 75 | 76 | export const css =

(node: FC | keyof InternalIntrinsicElements) => ( 77 | strings: TemplateStringsArray, 78 | ...args: string[] 79 | ): FC

> => { 80 | const className = randomString(); 81 | const style = strings.reduce( 82 | (acc, cur, idx) => (args[idx] ? acc + cur + args[idx] : acc + cur), 83 | "" 84 | ); 85 | const Component: FC

= (props) => { 86 | if (!currentContext) 87 | throw new Error("you should create context, before render"); 88 | currentContext.map.set(className, style); 89 | // @ts-expect-error [2769] No overload matches this call. 90 | return h(node, { className, ...props }); 91 | }; 92 | return Component; 93 | }; 94 | -------------------------------------------------------------------------------- /packages/lemon/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./jsx"; 2 | export * from "./renderToString"; 3 | export * from "./createElement"; 4 | 5 | import * as css from "./css"; 6 | 7 | export const styled = css.css; 8 | export const renderToStyleString = css.renderToStyleString; 9 | export const createStyleContext = css.createStyleContext; 10 | export const refreshContext = css.refreshContext; 11 | export type StyleContext = css.Context; 12 | -------------------------------------------------------------------------------- /packages/lemon/src/jsx.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://www.w3.org/TR/html52/dom.html#global-attributes-2 3 | * @description 4 | * GlobalAttributes types based on html5 spec 5 | * W3C Semantics, structure, and APIs of HTML documents 6 | * 3.2.5. 7 | */ 8 | 9 | import { AutoComplete } from "./autocomplete"; 10 | import { RFC2978 } from "./rfc2978"; 11 | import { Role } from "./role"; 12 | import { WAIAria } from "./wai-aria"; 13 | 14 | type WhatWGMicroData = Partial<{ 15 | itemid: string; 16 | itemprop: string; 17 | itemref: string; 18 | itemscope: string; 19 | itemtype: string; 20 | }>; 21 | 22 | export type GlobalAttributes = Partial<{ 23 | accesskey: string; 24 | contenteditable: string; 25 | dir: string; 26 | draggable: string; 27 | hidden: string; 28 | id: string; 29 | lang: string; 30 | spellcheck: string; 31 | style: string; 32 | tabindex: string; 33 | title: string; 34 | translate: string; 35 | class: string; 36 | }> & 37 | WhatWGMicroData & 38 | WAIAria & 39 | Partial<{ role: Role }>; 40 | 41 | export interface InternalIntrinsicElements { 42 | // メインルート 43 | html: GlobalAttributes & 44 | Partial<{ 45 | manifest: string; 46 | }>; 47 | 48 | // 文書メタデータ 49 | base: GlobalAttributes & 50 | Partial<{ 51 | href: string; 52 | target: string; 53 | }>; 54 | head: GlobalAttributes; 55 | link: GlobalAttributes & 56 | Partial<{ 57 | href: string; 58 | crossorigin: string; 59 | rel: string; 60 | media: string; 61 | integrity: string; 62 | hreflang: string; 63 | type: string; 64 | referrerpolicy: string; 65 | sizes: string; 66 | imagesrcset: string; 67 | imagesizes: string; 68 | as: string; 69 | color: string; 70 | disable: boolean; 71 | }>; 72 | meta: GlobalAttributes & 73 | Partial<{ 74 | name: string; 75 | "http-equiv": string; 76 | content: string; 77 | charset: RFC2978; 78 | property: string 79 | }>; 80 | style: GlobalAttributes & 81 | Partial<{ 82 | media: string; 83 | title: string; 84 | nonce: string; 85 | }>; 86 | title: GlobalAttributes; 87 | 88 | // セクショニングルート 89 | body: GlobalAttributes; 90 | 91 | // コンテンツセクショニング 92 | address: GlobalAttributes; 93 | article: GlobalAttributes; 94 | aside: GlobalAttributes; 95 | footer: GlobalAttributes; 96 | header: GlobalAttributes; 97 | h1: GlobalAttributes; 98 | h2: GlobalAttributes; 99 | h3: GlobalAttributes; 100 | h4: GlobalAttributes; 101 | h5: GlobalAttributes; 102 | h6: GlobalAttributes; 103 | hgroup: GlobalAttributes; 104 | main: GlobalAttributes; 105 | nav: GlobalAttributes; 106 | section: GlobalAttributes; 107 | 108 | // テキストコンテンツ 109 | blockquote: GlobalAttributes & 110 | Partial<{ 111 | cite: string; 112 | }>; 113 | dd: GlobalAttributes; 114 | dl: GlobalAttributes; 115 | dt: GlobalAttributes; 116 | div: GlobalAttributes; 117 | figcaption: GlobalAttributes; 118 | figure: GlobalAttributes; 119 | hr: GlobalAttributes; 120 | li: GlobalAttributes & 121 | Partial<{ 122 | value: number; 123 | }>; 124 | ol: GlobalAttributes & 125 | Partial<{ 126 | reversed: boolean; 127 | start: number; 128 | type: string; 129 | }>; 130 | p: GlobalAttributes; 131 | pre: GlobalAttributes; 132 | ul: GlobalAttributes; 133 | 134 | // インライン文字列意味付け 135 | a: GlobalAttributes & 136 | Partial<{ 137 | href: string; 138 | target: string; 139 | download: string; 140 | ping: string; 141 | rel: string; 142 | hreflang: string; 143 | type: string; 144 | }>; 145 | abbr: GlobalAttributes & 146 | Partial<{ 147 | title: string; 148 | }>; 149 | b: GlobalAttributes; 150 | bdi: GlobalAttributes & 151 | Partial<{ 152 | dir: "ltr" | "rtl" | "auto"; 153 | }>; 154 | bdo: GlobalAttributes & 155 | Partial<{ 156 | dir: "ltr" | "rtl" | "auto"; 157 | }>; 158 | br: GlobalAttributes; 159 | cite: GlobalAttributes; 160 | code: GlobalAttributes; 161 | data: GlobalAttributes & 162 | Partial<{ 163 | value: string; 164 | }>; 165 | dfn: GlobalAttributes & 166 | Partial<{ 167 | title: string; 168 | }>; 169 | em: GlobalAttributes; 170 | i: GlobalAttributes; 171 | kbd: GlobalAttributes; 172 | mark: GlobalAttributes; 173 | q: GlobalAttributes & 174 | Partial<{ 175 | cite: string; 176 | }>; 177 | rb: GlobalAttributes; 178 | rp: GlobalAttributes; 179 | rt: GlobalAttributes; 180 | rtc: GlobalAttributes; 181 | ruby: GlobalAttributes; 182 | s: GlobalAttributes; 183 | samp: GlobalAttributes; 184 | small: GlobalAttributes; 185 | span: GlobalAttributes; 186 | strong: GlobalAttributes; 187 | sub: GlobalAttributes; 188 | sup: GlobalAttributes; 189 | time: GlobalAttributes & 190 | Partial<{ 191 | datetime: string; 192 | }>; 193 | u: GlobalAttributes; 194 | var: GlobalAttributes; 195 | wbr: GlobalAttributes; 196 | 197 | // 画像とマルチメディア 198 | area: GlobalAttributes & 199 | Partial<{ 200 | alt: string; 201 | coords: string; 202 | shape: string; 203 | href: string; 204 | target: string; 205 | download: string; 206 | ping: string; 207 | rel: string; 208 | referrerpolicy: string; 209 | }>; 210 | audio: GlobalAttributes & 211 | Partial<{ 212 | src: string; 213 | crossorigin: string; 214 | preload: string; 215 | autoplay: string; 216 | loop: string; 217 | muted: string; 218 | controls: string; 219 | }>; 220 | img: GlobalAttributes & 221 | Partial<{ 222 | alt: string; 223 | src: string; 224 | srcset: string; 225 | sizes: string; 226 | crossorigin: string; 227 | usemap: string; 228 | ismap: string; 229 | width: string; 230 | height: string; 231 | referrerpolicy: string; 232 | decoding: string; 233 | loading: string; 234 | }>; 235 | map: GlobalAttributes & 236 | Partial<{ 237 | name: string; 238 | }>; 239 | track: GlobalAttributes & 240 | Partial<{ 241 | kind: string; 242 | src: string; 243 | srclang: string; 244 | label: string; 245 | default: string; 246 | }>; 247 | video: GlobalAttributes & 248 | Partial<{ 249 | src: string; 250 | crossorigin: string; 251 | poster: string; 252 | preload: string; 253 | autoplay: string; 254 | playsinline: string; 255 | loop: string; 256 | muted: string; 257 | controls: string; 258 | width: string; 259 | height: string; 260 | }>; 261 | 262 | // 埋め込みコンテンツ 263 | embed: GlobalAttributes & 264 | Partial<{ 265 | src: string; 266 | type: string; 267 | width: string; 268 | height: string; 269 | }>; 270 | iframe: GlobalAttributes & 271 | Partial<{ 272 | src: string; 273 | srcdoc: string; 274 | name: string; 275 | sandbox: string; 276 | allow: string; 277 | allowfullscreen: string; 278 | width: string; 279 | height: string; 280 | referrerpolicy: string; 281 | loading: string; 282 | }>; 283 | object: GlobalAttributes & 284 | Partial<{ 285 | data: string; 286 | type: string; 287 | name: string; 288 | usemap: string; 289 | form: string; 290 | width: string; 291 | height: string; 292 | }>; 293 | param: GlobalAttributes & 294 | Partial<{ 295 | name: string; 296 | value: string; 297 | }>; 298 | picture: GlobalAttributes; 299 | source: GlobalAttributes & 300 | Partial<{ 301 | src: string; 302 | type: string; 303 | srcset: string; 304 | sizes: string; 305 | media: string; 306 | }>; 307 | 308 | // スクリプティング 309 | canvas: GlobalAttributes & 310 | Partial<{ 311 | width: string; 312 | height: string; 313 | }>; 314 | noscript: GlobalAttributes; 315 | script: GlobalAttributes & 316 | Partial<{ 317 | src: string; 318 | type: string; 319 | nomodule: boolean; 320 | async: boolean; 321 | defer: boolean; 322 | nonce: string; 323 | crossorigin: string; 324 | integrity: string; 325 | referrerpolicy: string; 326 | }>; 327 | 328 | // 編集範囲の特定 329 | del: GlobalAttributes & 330 | Partial<{ 331 | cite: string; 332 | datetime: string; 333 | }>; 334 | ins: GlobalAttributes & 335 | Partial<{ 336 | cite: string; 337 | datetime: string; 338 | }>; 339 | 340 | // table 341 | caption: GlobalAttributes; 342 | col: GlobalAttributes & Partial<{ span: number }>; 343 | colgroup: GlobalAttributes & Partial<{ span: number }>; 344 | table: GlobalAttributes; 345 | tbody: GlobalAttributes; 346 | thead: GlobalAttributes; 347 | tfoot: GlobalAttributes; 348 | td: GlobalAttributes & 349 | Partial<{ 350 | colspan: string; 351 | rowspan: string; 352 | headers: string; 353 | }>; 354 | th: GlobalAttributes & 355 | Partial<{ 356 | colspan: string; 357 | rowspan: string; 358 | headers: string; 359 | scope: string; 360 | abbr: string; 361 | }>; 362 | tr: GlobalAttributes; 363 | 364 | // form 365 | button: GlobalAttributes & 366 | Partial<{ 367 | disabled: string; 368 | form: string; 369 | formaction: string; 370 | formenctype: string; 371 | formmethod: string; 372 | formnovalidate: string; 373 | formtarget: string; 374 | name: string; 375 | type: string; 376 | value: string; 377 | }>; 378 | datalist: GlobalAttributes; 379 | fieldset: GlobalAttributes & 380 | Partial<{ 381 | disabled: string; 382 | form: string; 383 | name: string; 384 | }>; 385 | form: GlobalAttributes & 386 | Partial<{ 387 | "accept-charset": RFC2978; 388 | action: string; 389 | autocomplete: AutoComplete; 390 | enctype: string; 391 | method: string; 392 | name: string; 393 | novalidate: string; 394 | target: string; 395 | rel: string; 396 | }>; 397 | input: GlobalAttributes & 398 | Partial<{ 399 | accept: string; 400 | alt: string; 401 | autocomplete: AutoComplete; 402 | checked: string; 403 | dirname: string; 404 | disabled: boolean; 405 | form: string; 406 | formaction: string; 407 | formenctype: string; 408 | formmethod: string; 409 | formnovalidate: string; 410 | formtarget: string; 411 | height: string; 412 | list: string; 413 | max: string; 414 | maxlength: string; 415 | min: string; 416 | minlength: string; 417 | multiple: string; 418 | name: string; 419 | pattern: string; 420 | placeholder: string; 421 | readonly: boolean; 422 | required: boolean; 423 | size: string; 424 | src: string; 425 | step: string; 426 | type: string; 427 | value: string; 428 | width: string; 429 | }>; 430 | label: GlobalAttributes & 431 | Partial<{ 432 | for: string; 433 | }>; 434 | legend: GlobalAttributes; 435 | meter: GlobalAttributes & 436 | Partial<{ 437 | value: number; 438 | min: number; 439 | max: number; 440 | low: number; 441 | high: number; 442 | optimum: number; 443 | }>; 444 | optgroup: GlobalAttributes & 445 | Partial<{ 446 | disabled: boolean; 447 | label: string; 448 | }>; 449 | option: GlobalAttributes & 450 | Partial<{ 451 | disabled: boolean; 452 | label: string; 453 | }>; 454 | output: GlobalAttributes & 455 | Partial<{ 456 | for: string; 457 | form: string; 458 | name: string; 459 | }>; 460 | progress: GlobalAttributes & 461 | Partial<{ 462 | value: number; 463 | max: number; 464 | }>; 465 | select: GlobalAttributes & 466 | Partial<{ 467 | autocomplete: AutoComplete; 468 | disabled: boolean; 469 | form: string; 470 | multiple: string; 471 | name: string; 472 | required: boolean; 473 | size: string; 474 | }>; 475 | textarea: GlobalAttributes & 476 | Partial<{ 477 | autocomplete: AutoComplete; 478 | cols: number; 479 | dirname: string; 480 | disabled: boolean; 481 | form: string; 482 | maxlength: number; 483 | minlength: number; 484 | name: string; 485 | placeholder: string; 486 | readonly: boolean; 487 | required: boolean; 488 | rows: number; 489 | wrap: number; 490 | }>; 491 | 492 | // interactive 493 | details: GlobalAttributes & 494 | Partial<{ 495 | open: boolean; 496 | }>; 497 | dialog: GlobalAttributes & 498 | Partial<{ 499 | open: boolean; 500 | }>; 501 | menu: GlobalAttributes; 502 | summary: GlobalAttributes; 503 | } 504 | 505 | export interface FC

{ 506 | (props: P & { children?: ComponentChildren }): VNode | null; 507 | } 508 | 509 | export type Children = VNode | string | null; 510 | export type ComponentChildren = Children[] | Children; 511 | 512 | export interface VNode

{ 513 | key?: string | number; 514 | type: FC

| string; 515 | props: P & { children?: ComponentChildren }; 516 | } 517 | -------------------------------------------------------------------------------- /packages/lemon/src/renderToString.ts: -------------------------------------------------------------------------------- 1 | import { VNode } from "."; 2 | 3 | const arrayToString = (node: VNode<{ children: any[] }>): string => { 4 | return node.props.children.map(renderToString).join(""); 5 | }; 6 | 7 | const attrToString = ([key, value]: [string, unknown]): string => { 8 | switch (key) { 9 | case "async": case "defer": 10 | return !!value ? key : ''; 11 | case "className": 12 | return `class="${value}"`; 13 | case "aria-relevant": 14 | return `aria-relevant="${(value as Array).join(" ")}"`; 15 | default: 16 | return `${key}="${value}"`; 17 | } 18 | }; 19 | 20 | export function renderToString(node: Array> | VNode | string | null): string { 21 | if (!node) return ""; 22 | if (typeof node === "string") return node; 23 | if (Array.isArray(node)) { 24 | return node.map(renderToString).join("") 25 | } 26 | const childrenAsArray = 27 | typeof node.props === "object" && 28 | node.props != null && 29 | node.props.children && 30 | node.props.children.length >= 1; 31 | if (typeof node.type === "string") { 32 | const attr = Object.entries(node.props) 33 | .filter(([k, _]) => k !== "children") 34 | .map(attrToString) 35 | .join(" ") 36 | .trim(); 37 | const typeAndAttr = `${node.type} ${attr}`.trim(); 38 | if (childrenAsArray) { 39 | return `<${typeAndAttr}>${arrayToString(node)}`; 40 | } 41 | return `<${typeAndAttr}>`; 42 | } 43 | if (childrenAsArray) { 44 | const { children, ...props } = node.props; 45 | return renderToString( 46 | node.type({ 47 | ...props, 48 | children: children.map((c: any) => { 49 | return renderToString(c); 50 | }), 51 | }) 52 | ); 53 | } 54 | return renderToString(node.type(node.props)); 55 | } 56 | -------------------------------------------------------------------------------- /packages/lemon/src/rfc2978.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://www.iana.org/assignments/character-sets/character-sets.xhtml 3 | */ 4 | export type RFC2978 = 5 | | "US-ASCII" 6 | | "ISO_8859-1:1987" 7 | | "ISO_8859-2:1987" 8 | | "ISO_8859-3:1988" 9 | | "ISO_8859-4:1988" 10 | | "ISO_8859-5:1988" 11 | | "ISO_8859-6:1987" 12 | | "ISO_8859-7:1987" 13 | | "ISO_8859-8:1988" 14 | | "ISO_8859-9:1989" 15 | | "ISO-8859-10" 16 | | "ISO_6937-2-add" 17 | | "JIS_X0201" 18 | | "JIS_Encoding" 19 | | "Shift_JIS" 20 | | "Extended_UNIX_Code_Packed_Format_for_Japanese" 21 | | "Extended_UNIX_Code_Fixed_Width_for_Japanese" 22 | | "BS_4730" 23 | | "SEN_850200_C" 24 | | "IT" 25 | | "ES" 26 | | "DIN_66003" 27 | | "NS_4551-1" 28 | | "NF_Z_62-010" 29 | | "ISO-10646-UTF-1" 30 | | "ISO_646.basic:1983" 31 | | "INVARIANT" 32 | | "ISO_646.irv:1983" 33 | | "NATS-SEFI" 34 | | "NATS-SEFI-ADD" 35 | | "NATS-DANO" 36 | | "NATS-DANO-ADD" 37 | | "SEN_850200_B" 38 | | "KS_C_5601-1987" 39 | | "ISO-2022-KR" 40 | | "EUC-KR" 41 | | "ISO-2022-JP" 42 | | "ISO-2022-JP-2" 43 | | "JIS_C6220-1969-jp" 44 | | "JIS_C6220-1969-ro" 45 | | "PT" 46 | | "greek7-old" 47 | | "latin-greek" 48 | | "NF_Z_62-010_(1973)" 49 | | "Latin-greek-1" 50 | | "ISO_5427" 51 | | "JIS_C6226-1978" 52 | | "BS_viewdata" 53 | | "INIS" 54 | | "INIS-8" 55 | | "INIS-cyrillic" 56 | | "ISO_5427:1981" 57 | | "ISO_5428:1980" 58 | | "GB_1988-80" 59 | | "GB_2312-80" 60 | | "NS_4551-2" 61 | | "videotex-suppl" 62 | | "PT2" 63 | | "ES2" 64 | | "MSZ_7795.3" 65 | | "JIS_C6226-1983" 66 | | "greek7" 67 | | "ASMO_449" 68 | | "iso-ir-90" 69 | | "JIS_C6229-1984-a" 70 | | "JIS_C6229-1984-b" 71 | | "JIS_C6229-1984-b-add" 72 | | "JIS_C6229-1984-hand" 73 | | "JIS_C6229-1984-hand-add" 74 | | "JIS_C6229-1984-kana" 75 | | "ISO_2033-1983" 76 | | "ANSI_X3.110-1983" 77 | | "T.61-7bit" 78 | | "T.61-8bit" 79 | | "ECMA-cyrillic" 80 | | "CSA_Z243.4-1985-1" 81 | | "CSA_Z243.4-1985-2" 82 | | "CSA_Z243.4-1985-gr" 83 | | "ISO_8859-6-E" 84 | | "ISO_8859-6-I" 85 | | "T.101-G2" 86 | | "ISO_8859-8-E" 87 | | "ISO_8859-8-I" 88 | | "CSN_369103" 89 | | "JUS_I.B1.002" 90 | | "IEC_P27-1" 91 | | "JUS_I.B1.003-serb" 92 | | "JUS_I.B1.003-mac" 93 | | "greek-ccitt" 94 | | "NC_NC00-10:81" 95 | | "ISO_6937-2-25" 96 | | "GOST_19768-74" 97 | | "ISO_8859-supp" 98 | | "ISO_10367-box" 99 | | "latin-lap" 100 | | "JIS_X0212-1990" 101 | | "DS_2089" 102 | | "us-dk" 103 | | "dk-us" 104 | | "KSC5636" 105 | | "UNICODE-1-1-UTF-7" 106 | | "ISO-2022-CN" 107 | | "ISO-2022-CN-EXT" 108 | | "UTF-8" 109 | | "ISO-8859-13" 110 | | "ISO-8859-14" 111 | | "ISO-8859-15" 112 | | "ISO-8859-16" 113 | | "GBK" 114 | | "GB18030" 115 | | "OSD_EBCDIC_DF04_15" 116 | | "OSD_EBCDIC_DF03_IRV" 117 | | "OSD_EBCDIC_DF04_1" 118 | | "ISO-11548-1" 119 | | "KZ-1048" 120 | | "ISO-10646-UCS-2" 121 | | "ISO-10646-UCS-4" 122 | | "ISO-10646-UCS-Basic" 123 | | "ISO-10646-Unicode-Latin1" 124 | | "ISO-10646-J-1" 125 | | "ISO-Unicode-IBM-1261" 126 | | "ISO-Unicode-IBM-1268" 127 | | "ISO-Unicode-IBM-1276" 128 | | "ISO-Unicode-IBM-1264" 129 | | "ISO-Unicode-IBM-1265" 130 | | "UNICODE-1-1" 131 | | "SCSU" 132 | | "UTF-7" 133 | | "UTF-16BE" 134 | | "UTF-16LE" 135 | | "UTF-16" 136 | | "CESU-8" 137 | | "UTF-32" 138 | | "UTF-32BE" 139 | | "UTF-32LE" 140 | | "BOCU-1" 141 | | "ISO-8859-1-Windows-3.0-Latin-1" 142 | | "ISO-8859-1-Windows-3.1-Latin-1" 143 | | "ISO-8859-2-Windows-Latin-2" 144 | | "ISO-8859-9-Windows-Latin-5" 145 | | "hp-roman8" 146 | | "Adobe-Standard-Encoding" 147 | | "Ventura-US" 148 | | "Ventura-International" 149 | | "DEC-MCS" 150 | | "IBM850" 151 | | "PC8-Danish-Norwegian" 152 | | "IBM862" 153 | | "PC8-Turkish" 154 | | "IBM-Symbols" 155 | | "IBM-Thai" 156 | | "HP-Legal" 157 | | "HP-Pi-font" 158 | | "HP-Math8" 159 | | "Adobe-Symbol-Encoding" 160 | | "HP-DeskTop" 161 | | "Ventura-Math" 162 | | "Microsoft-Publishing" 163 | | "Windows-31J" 164 | | "GB2312" 165 | | "Big5" 166 | | "macintosh" 167 | | "IBM037" 168 | | "IBM038" 169 | | "IBM273" 170 | | "IBM274" 171 | | "IBM275" 172 | | "IBM277" 173 | | "IBM278" 174 | | "IBM280" 175 | | "IBM281" 176 | | "IBM284" 177 | | "IBM285" 178 | | "IBM290" 179 | | "IBM297" 180 | | "IBM420" 181 | | "IBM423" 182 | | "IBM424" 183 | | "IBM437" 184 | | "IBM500" 185 | | "IBM851" 186 | | "IBM852" 187 | | "IBM855" 188 | | "IBM857" 189 | | "IBM860" 190 | | "IBM861" 191 | | "IBM863" 192 | | "IBM864" 193 | | "IBM865" 194 | | "IBM868" 195 | | "IBM869" 196 | | "IBM870" 197 | | "IBM871" 198 | | "IBM880" 199 | | "IBM891" 200 | | "IBM903" 201 | | "IBM904" 202 | | "IBM905" 203 | | "IBM918" 204 | | "IBM1026" 205 | | "EBCDIC-AT-DE" 206 | | "EBCDIC-AT-DE-A" 207 | | "EBCDIC-CA-FR" 208 | | "EBCDIC-DK-NO" 209 | | "EBCDIC-DK-NO-A" 210 | | "EBCDIC-FI-SE" 211 | | "EBCDIC-FI-SE-A" 212 | | "EBCDIC-FR" 213 | | "EBCDIC-IT" 214 | | "EBCDIC-PT" 215 | | "EBCDIC-ES" 216 | | "EBCDIC-ES-A" 217 | | "EBCDIC-ES-S" 218 | | "EBCDIC-UK" 219 | | "EBCDIC-US" 220 | | "UNKNOWN-8BIT" 221 | | "MNEMONIC" 222 | | "MNEM" 223 | | "VISCII" 224 | | "VIQR" 225 | | "KOI8-R" 226 | | "HZ-GB-2312" 227 | | "IBM866" 228 | | "IBM775" 229 | | "KOI8-U" 230 | | "IBM00858" 231 | | "IBM00924" 232 | | "IBM01140" 233 | | "IBM01141" 234 | | "IBM01142" 235 | | "IBM01143" 236 | | "IBM01144" 237 | | "IBM01145" 238 | | "IBM01146" 239 | | "IBM01147" 240 | | "IBM01148" 241 | | "IBM01149" 242 | | "Big5-HKSCS" 243 | | "IBM1047" 244 | | "PTCP154" 245 | | "Amiga-1251" 246 | | "KOI7-switched" 247 | | "BRF" 248 | | "TSCII" 249 | | "CP51932" 250 | | "windows-874" 251 | | "windows-1250" 252 | | "windows-1251" 253 | | "windows-1252" 254 | | "windows-1253" 255 | | "windows-1254" 256 | | "windows-1255" 257 | | "windows-1256" 258 | | "windows-1257" 259 | | "windows-1258" 260 | | "TIS-620" 261 | | "CP50220"; 262 | 263 | -------------------------------------------------------------------------------- /packages/lemon/src/role.ts: -------------------------------------------------------------------------------- 1 | export type Role = 2 | | "alert" 3 | | "alertdialog" 4 | | "application" 5 | | "article" 6 | | "banner" 7 | | "button" 8 | | "button" 9 | | "cell" 10 | | "cell" 11 | | "checkbox" 12 | | "checkbox" 13 | | "columnheader" 14 | | "columnheader" 15 | | "combobox" 16 | | "command" 17 | | "complementary" 18 | | "composite" 19 | | "contentinfo" 20 | | "definition" 21 | | "dialog" 22 | | "directory" 23 | | "document" 24 | | "feed" 25 | | "figure" 26 | | "form" 27 | | "grid" 28 | | "gridcell" 29 | | "gridcell" 30 | | "group" 31 | | "heading" 32 | | "heading" 33 | | "img" 34 | | "input" 35 | | "landmark" 36 | | "link" 37 | | "link" 38 | | "list" 39 | | "listbox" 40 | | "listitem" 41 | | "log" 42 | | "main" 43 | | "marquee" 44 | | "math" 45 | | "menu" 46 | | "menubar" 47 | | "menuitem" 48 | | "menuitem" 49 | | "menuitemcheckbox" 50 | | "menuitemcheckbox" 51 | | "menuitemradio" 52 | | "menuitemradio" 53 | | "navigation" 54 | | "none" 55 | | "note" 56 | | "option" 57 | | "option" 58 | | "presentation" 59 | | "progressbar" 60 | | "radio" 61 | | "radio" 62 | | "radiogroup" 63 | | "range" 64 | | "region" 65 | | "roletype" 66 | | "row" 67 | | "row" 68 | | "rowgroup" 69 | | "rowgroup" 70 | | "rowheader" 71 | | "rowheader" 72 | | "scrollbar" 73 | | "search" 74 | | "searchbox" 75 | | "section" 76 | | "sectionhead" 77 | | "select" 78 | | "separator" 79 | | "separator" 80 | | "slider" 81 | | "spinbutton" 82 | | "status" 83 | | "structure" 84 | | "switch" 85 | | "switch" 86 | | "tab" 87 | | "tab" 88 | | "table" 89 | | "tablist" 90 | | "tabpanel" 91 | | "term" 92 | | "textbox" 93 | | "timer" 94 | | "toolbar" 95 | | "tooltip" 96 | | "tooltip" 97 | | "tree" 98 | | "tree" 99 | | "treegrid" 100 | | "treeitem" 101 | | "treeitem" 102 | | "widget" 103 | | "window"; 104 | -------------------------------------------------------------------------------- /packages/lemon/src/wai-aria.ts: -------------------------------------------------------------------------------- 1 | type TriState = "true" | "false" | "mixed" | "undefined"; // default "undefined" 2 | 3 | /** 4 | * refs: https://www.w3.org/TR/2014/REC-wai-aria-20140320/states_and_properties.html 5 | */ 6 | export type WAIAria = Partial<{ 7 | "aria-activedescendant": ""; 8 | "aria-atomic": "true" | "false"; // defalut "false" 9 | "aria-autocomplete": "inline" | "list" | "both" | "none"; // default "none" 10 | "aria-busy": "true" | "false"; // default "false" 11 | "aria-checked": TriState; 12 | "aria-controls": string; 13 | "aria-describedby": string; 14 | "aria-disabled": "true" | "false"; // default "false" 15 | "aria-dropeffect": "copy" | "move" | "link" | "execute" | "popup" | "none"; // default "none" 16 | "aria-expanded": "true" | "false" | "undefined"; // default "undefined" 17 | "aria-flowto": string; 18 | "aria-grabbed": "true" | "false" | "undefined"; // default "undefined" 19 | "aria-haspopup": "true" | "false"; // default "false" 20 | "aria-hidden": "true" | "false"; // default "false" 21 | "aria-invalid": "grammar" | "false" | "spelling" | "true"; // default "false" 22 | "aria-label": string; 23 | "aria-labelledby": string; 24 | "aria-level": number; 25 | "aria-live": "log" | "status" | "alert" | "progressbar" | "marquee" | "timer"; 26 | "aria-multiline": "true" | "false"; // default "false" 27 | "aria-multiselectable": "true" | "false"; // default "false" 28 | "aria-orientation": "vertical" | "horizontal"; // default "horizontal" 29 | "aria-owns": string; 30 | "aria-posinset": number; 31 | "aria-pressed": TriState; 32 | "aria-readonly": "true" | "false"; // default "false" 33 | "aria-relevant": Array<"additions" | "removals" | "text" | "all">; // default "additions text" 34 | "aria-required": "true" | "false"; 35 | "aria-selected": "true" | "false" | "undefined"; // default "undefined"; 36 | "aria-setsize": number; 37 | "aria-sort": "ascending" | "descending" | "none" | "other"; // default "none" 38 | "aria-valuemax": number; 39 | "aria-valuemin": number; 40 | "aria-valuenow": number; 41 | "aria-valuetext": string; 42 | }>; 43 | -------------------------------------------------------------------------------- /packages/lemon/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "rootDir": "./src", 6 | "outDir": "./esm" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/lemon/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "rootDir": "./src", 6 | "outDir": "./build", 7 | "jsx": "react", 8 | "jsxFactory": "h", 9 | "jsxFragmentFactory": "Fragment" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/melon/.gitignore: -------------------------------------------------------------------------------- 1 | *.0x 2 | *.pem 3 | /for-local/nginx.conf 4 | *.log 5 | -------------------------------------------------------------------------------- /packages/melon/README.md: -------------------------------------------------------------------------------- 1 | # :melon: kajitsu:melon - 果実:めろん 2 | > めろんのブログ 3 | 4 | ## LICENSE 5 | 6 | MIT 7 | -------------------------------------------------------------------------------- /packages/melon/for-local/nginx.templ.conf: -------------------------------------------------------------------------------- 1 | worker_processes auto; 2 | 3 | error_log $GHQ_ROOT/github.com/maxmellon/kajitsu/packages/melon/log/error.log; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | default_type application/octet-stream; 11 | server_tokens off; 12 | 13 | log_format main 'time:$time_iso8601\t' 14 | 'protocol:$server_protocol\t' 15 | 'method:$request_method\t' 16 | 'path:$request_uri\t' 17 | 'status:$status\t' 18 | 'request_time:$request_time\t' 19 | 'upstream_response_time:$upstream_response_time\t' 20 | 'body_bytes_sent:$body_bytes_sent\t' 21 | 'remote_addr:$remote_addr\t' 22 | 'x_forwarded_for:$http_x_forwarded_for\t' 23 | 'referer:$http_referer\t' 24 | 'user_agent:$http_user_agent\t'; 25 | 26 | access_log $GHQ_ROOT/github.com/maxmellon/kajitsu/packages/melon/log/access.log main; 27 | 28 | upstream app { 29 | server 127.0.0.1:3000; 30 | keepalive 32; 31 | } 32 | 33 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 34 | 35 | server { 36 | listen 443 ssl http2; 37 | ssl_certificate $GHQ_ROOT/github.com/maxmellon/kajitsu/packages/melon/localhost.pem; 38 | ssl_certificate_key $GHQ_ROOT/github.com/maxmellon/kajitsu/packages/melon/localhost-key.pem; 39 | 40 | proxy_connect_timeout 5; 41 | proxy_send_timeout 30; 42 | proxy_read_timeout 30; 43 | proxy_intercept_errors on; 44 | proxy_hide_header X-Powered-By; 45 | 46 | location / { 47 | add_header Feature-Policy "sync-xhr 'none'"; 48 | add_header X-Frame-Options "SAMEORIGIN"; 49 | add_header X-XSS-Protection "1; mode=block"; 50 | add_header X-Content-Type-Options "nosniff"; 51 | add_header Strict-Transport-Security "max-age=31536000"; 52 | 53 | proxy_pass http://app; 54 | } 55 | } 56 | 57 | server { 58 | listen 80; 59 | return 301 https://$host$request_uri; 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /packages/melon/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/log/.keep -------------------------------------------------------------------------------- /packages/melon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@kajitsu/melon", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "prebuild": "rm -rf build tsconfig.tsbuildinfo && cp -r src build", 8 | "build": "tsc -d -p ./tsconfig.json", 9 | "test": "echo TODO", 10 | "run": "npx nodemon -e js,ts,tsx,css --watch ./src --exec \"npx ts-node ./src/index.tsx\"", 11 | "production": "NODE_ENV=production node ./build/index.js", 12 | "nginx": "envsubst '\\$GHQ_ROOT' < ./for-local/nginx.templ.conf > ./for-local/nginx.conf && nginx -c $(pwd)/for-local/nginx.conf -g 'daemon off;'" 13 | }, 14 | "author": "", 15 | "license": "MIT", 16 | "dependencies": { 17 | "@kajitsu/ichigo": "1.0.0", 18 | "@kajitsu/suica": "1.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/melon/src/assets/css/markdown.css: -------------------------------------------------------------------------------- 1 | #mr { 2 | padding-bottom: 50px; 3 | } 4 | 5 | #mr h1 { 6 | font-size: 32px; 7 | margin: 20px 0; 8 | } 9 | 10 | #mr h1::before { 11 | font-size: 28px; 12 | content: '#'; 13 | margin-right: 20px; 14 | } 15 | 16 | #mr blockquote { 17 | padding: 10px; 18 | } 19 | 20 | #mr blockquote > p::before { 21 | display: inline; 22 | width: 4px; 23 | height: 2em; 24 | background-color: #eee; 25 | content: ' '; 26 | position: absolute; 27 | margin-left: -20px; 28 | } 29 | 30 | #mr blockquote > p { 31 | padding-left: 20px; 32 | } 33 | 34 | @media (prefers-color-scheme: dark) { 35 | #mr blockquote > p::before { 36 | background-color: #000; 37 | } 38 | } 39 | 40 | #mr h2 { 41 | font-size: 28px; 42 | margin: 16px 0; 43 | } 44 | 45 | #mr h2::before { 46 | font-size: 24px; 47 | content: '##'; 48 | margin-right: 16px; 49 | } 50 | 51 | #mr h3 { 52 | font-size: 20px; 53 | margin: 12px 0; 54 | } 55 | 56 | #mr h4 { 57 | font-size: 18px; 58 | margin: 12px 0; 59 | } 60 | 61 | #mr h4::before { 62 | font-size: 14px; 63 | content: '**'; 64 | margin: 8px 0; 65 | } 66 | 67 | #mr h4::after { 68 | font-size: 14px; 69 | content: '**'; 70 | margin: 8px 0; 71 | } 72 | 73 | #mr strong { 74 | margin: 12px 0; 75 | font-weight: 800; 76 | } 77 | 78 | #mr strong::before { 79 | content: '**'; 80 | margin: 3px 0; 81 | } 82 | 83 | #mr strong::after { 84 | content: '**'; 85 | margin: 3px 0; 86 | } 87 | 88 | #mr em { 89 | font-weight: 700; 90 | } 91 | 92 | #mr h3::before { 93 | font-size: 16px; 94 | content: '###'; 95 | margin-right: 8px; 96 | } 97 | 98 | #mr pre::before { 99 | display: block; 100 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 101 | content: '```'; 102 | height: 10px; 103 | margin-top: 20px; 104 | } 105 | 106 | #mr pre::after { 107 | display: block; 108 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 109 | content: '```'; 110 | margin-bottom: 20px; 111 | } 112 | 113 | #mr code { 114 | display: inline; 115 | background-color: #eee; 116 | padding: 2px 8px; 117 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 118 | white-space: nowrap; 119 | } 120 | 121 | @media all and (max-width: 800px) { 122 | #mr code { 123 | white-space: pre-wrap; 124 | } 125 | } 126 | 127 | #mr code::before { 128 | display: inline; 129 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 130 | content: '`'; 131 | } 132 | 133 | #mr code::after { 134 | display: inline; 135 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 136 | content: '`'; 137 | } 138 | 139 | #mr pre > code::after { 140 | content: ''; 141 | } 142 | 143 | #mr pre > code::before { 144 | content: ''; 145 | } 146 | 147 | @media (prefers-color-scheme: dark) { 148 | #mr code { 149 | background-color: #000; 150 | } 151 | } 152 | 153 | #mr pre > code { 154 | display: block; 155 | background-color: #eee; 156 | border-left: 4px solid #353; 157 | padding: 20px; 158 | margin: 10px 5px; 159 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 160 | overflow: scroll; 161 | white-space: pre; 162 | } 163 | 164 | @media (prefers-color-scheme: dark) { 165 | #mr pre > code { 166 | background-color: #000; 167 | } 168 | } 169 | 170 | #mr ul { 171 | margin: 10px 0; 172 | list-style-type: none; 173 | margin-left: 16px; 174 | } 175 | 176 | #mr ul > li::before { 177 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 178 | font-size: 16px; 179 | margin-right: 16px; 180 | content: '*'; 181 | } 182 | 183 | #mr ul > li > ul { 184 | margin: 10px 0; 185 | list-style-type: none; 186 | margin-left: 26px; 187 | } 188 | 189 | #mr ul > li > ul > li::before { 190 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 191 | font-size: 16px; 192 | margin-right: 16px; 193 | content: '*'; 194 | } 195 | -------------------------------------------------------------------------------- /packages/melon/src/assets/css/reset.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, 2 | blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, 3 | img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, 4 | i, center, hr, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, 5 | caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, 6 | embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, 7 | section, summary, time, mark, audio, video { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | font: inherit; 12 | vertical-align: baseline; 13 | } 14 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 15 | display: block; 16 | } 17 | ol, ul { 18 | list-style: none; 19 | } 20 | blockquote, q { 21 | quotes: none; 22 | } 23 | blockquote::before, blockquote::after, q::before, q::after { 24 | content: ''; 25 | } 26 | table { 27 | border-collapse: collapse; 28 | border-spacing: 0; 29 | } 30 | img { 31 | max-width: 100%; 32 | border-style: none; 33 | } 34 | :disabled { 35 | cursor: not-allowed; 36 | } 37 | * { 38 | box-sizing: border-box; 39 | } 40 | *::before, *::after { 41 | box-sizing: inherit; 42 | } 43 | body { 44 | background: transparent; 45 | font-family: "Helvetica Neue", 46 | Arial, 47 | "Hiragino Kaku Gothic ProN", 48 | "Hiragino Sans", 49 | Meiryo, 50 | sans-serif; 51 | font-size: 16px; 52 | line-height: 1.75; 53 | } 54 | h1, h2, h3, h4, h5, h6 { 55 | font-weight: 700; 56 | } 57 | button { 58 | border: none; 59 | padding: 0; 60 | background: none; 61 | color: inherit; 62 | font: inherit; 63 | appearance: none; 64 | box-shadow: 0 2px 8px 0 rgba(24, 74, 70, 0.2); 65 | text-shadow: 0 2px 8px 0 rgba(24, 74, 70, 0.2); 66 | } 67 | 68 | button:active { 69 | box-shadow: none; 70 | text-shadow: none; 71 | transform: translateY(1px); 72 | } 73 | 74 | -------------------------------------------------------------------------------- /packages/melon/src/assets/css/theme.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --header-bg-color: #b0dab0; 3 | --header-color: #111; 4 | --main-bg-color: #fff; 5 | --main-color: #111; 6 | } 7 | 8 | a:visited { 9 | color: darkviolet; 10 | } 11 | 12 | a { 13 | color: darkblue; 14 | } 15 | 16 | @media (prefers-color-scheme: dark) { 17 | :root { 18 | --header-bg-color: #353; 19 | --header-color: #e3ffe5; 20 | --main-bg-color: #222; 21 | --main-color: #fff; 22 | } 23 | 24 | a:visited { 25 | color: coral; 26 | } 27 | 28 | a { 29 | color: cyan; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/melon/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/favicon.ico -------------------------------------------------------------------------------- /packages/melon/src/assets/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/icons/icon-128x128.png -------------------------------------------------------------------------------- /packages/melon/src/assets/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/icons/icon-144x144.png -------------------------------------------------------------------------------- /packages/melon/src/assets/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/icons/icon-152x152.png -------------------------------------------------------------------------------- /packages/melon/src/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /packages/melon/src/assets/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/icons/icon-384x384.png -------------------------------------------------------------------------------- /packages/melon/src/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /packages/melon/src/assets/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/icons/icon-72x72.png -------------------------------------------------------------------------------- /packages/melon/src/assets/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/icons/icon-96x96.png -------------------------------------------------------------------------------- /packages/melon/src/assets/images/loading.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/melon/src/assets/images/ogp-pipeline-operator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/images/ogp-pipeline-operator.png -------------------------------------------------------------------------------- /packages/melon/src/assets/images/states-of-js-2020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxMEllon/kajitsu/89c316415d6039f208eb60133b90aafb7b786a49/packages/melon/src/assets/images/states-of-js-2020.png -------------------------------------------------------------------------------- /packages/melon/src/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "me1on's blog", 3 | "name": "maxmellon's blog", 4 | "icons": [ 5 | { 6 | "src": "/icon-192x192.png", 7 | "type": "image/png", 8 | "sizes": "192x192", 9 | "purpose": "any maskable" 10 | }, 11 | { 12 | "src": "/icon-512x512.png", 13 | "type": "image/png", 14 | "sizes": "512x512" 15 | } 16 | ], 17 | "start_url": "/", 18 | "background_color": "#222", 19 | "display": "minimal-ui", 20 | "prefer_related_applications": true, 21 | "scope": "/", 22 | "theme_color": "#353" 23 | } 24 | -------------------------------------------------------------------------------- /packages/melon/src/assets/register.js: -------------------------------------------------------------------------------- 1 | if ("serviceWorker" in navigator) { 2 | navigator.serviceWorker.register("/sw.h6u4siOcagcPl8vd.js"); 3 | } 4 | -------------------------------------------------------------------------------- /packages/melon/src/assets/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | -------------------------------------------------------------------------------- /packages/melon/src/assets/sw.js: -------------------------------------------------------------------------------- 1 | const CACHE_NAME = "@kajitsu/melon@1.1.1"; 2 | 3 | const urlsToCache = ["/", "/blog/20201221", "/main.css", "/markdown.css"]; 4 | 5 | self.addEventListener("install", (event) => { 6 | event.waitUntil( 7 | caches.open(CACHE_NAME).then((cache) => { 8 | cache.addAll(urlsToCache); 9 | }), 10 | ); 11 | }); 12 | 13 | self.addEventListener("fetch", (event) => { 14 | if (!(event.request.url.indexOf("http") === 0)) { 15 | return event.respondWith(fetch(event.request)); 16 | } 17 | event.respondWith( 18 | fetch(event.request) 19 | .then((res) => 20 | caches.open(CACHE_NAME).then((cache) => { 21 | cache.put(event.request.url, res.clone()); 22 | return res; 23 | }), 24 | ) 25 | .catch(() => caches.match(event.request)), 26 | ); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/melon/src/components/organisms/nav/index.tsx: -------------------------------------------------------------------------------- 1 | import { h, FC, styled } from "@kajitsu/lemon"; 2 | 3 | export const Nav: FC = () => ( 4 | 5 | maxmellon's blog 6 | 7 | 8 | Blog 9 | 10 | 11 | 12 | ); 13 | 14 | const NavTitle = styled("h1")` 15 | font-size: 1.5rem; 16 | line-height: 1.7; 17 | display: inline-flex; 18 | `; 19 | 20 | const HtmlNav = styled("nav")` 21 | width: min(1000px, 95%); 22 | margin: 0 auto; 23 | height: 100%; 24 | `; 25 | const NavList = styled("ol")` 26 | display: inline-flex; 27 | vertical-align: top; 28 | height: 100%; 29 | line-height: 1.8; 30 | `; 31 | 32 | const NavListItem = styled("li")` 33 | padding-left: 3rem; 34 | line-height: 2.6; 35 | `; 36 | -------------------------------------------------------------------------------- /packages/melon/src/components/templates/html.tsx: -------------------------------------------------------------------------------- 1 | import { h, FC } from "@kajitsu/lemon"; 2 | 3 | type Props = { 4 | style?: string; 5 | title?: string; 6 | nonce?: string; 7 | extendsHead?: h.JSX.Element; 8 | }; 9 | 10 | export const Html: FC = ({ extendsHead, nonce, title, style, children }) => ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | {title && `${title} | `}maxmellon's blog 18 | 19 | 20 | 21 | {extendsHead} 22 | {nonce ? : } 23 | 24 | {children} 25 | {process.env.NODE_ENV === "production" && 873 | 874 | ); 875 | 876 | const lazyload = ` 877 | window.addEventListener("DOMContentLoaded", function() { 878 | var images = [].slice.call(document.querySelectorAll("img")); 879 | if ("IntersectionObserver" in window) { 880 | var lazyImageObserver = new IntersectionObserver(function(entries, observer) { 881 | entries.forEach(function(entry) { 882 | if (entry.isIntersecting) { 883 | var lazyImage = entry.target; 884 | lazyImage.src = lazyImage.dataset.src; 885 | lazyImageObserver.unobserve(lazyImage); 886 | } 887 | }); 888 | }); 889 | images.forEach(function(image) { 890 | lazyImageObserver.observe(image, { threshold: 0.2 }); 891 | }); 892 | } else { 893 | images.forEach(function(img) { img.src = img.dataset.src; }) 894 | } 895 | }, false) 896 | `; 897 | -------------------------------------------------------------------------------- /packages/melon/src/pages/blog/articles.tsx: -------------------------------------------------------------------------------- 1 | import { h, Fragment } from "@kajitsu/lemon"; 2 | import { PageTemplate } from "../../components/templates/page"; 3 | import { Nav } from "../../components/organisms/nav"; 4 | 5 | type Articles = { 6 | [year: string]: Array<{ 7 | title: string; 8 | date: string; 9 | key: string; 10 | extendsHeader: () => Promise; 11 | renderer: (nonce?: string) => Promise; 12 | }>; 13 | }; 14 | 15 | export const articles: Articles = { 16 | "2020": [ 17 | { 18 | title: "node modules に依存しない blog を作っている話", 19 | date: '2020-12-21', 20 | key: "20201221", 21 | extendsHeader: async () => { 22 | return ( 23 | <> 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | }, 31 | renderer: async () => { 32 | const header =