├── .devcontainer └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── COMMIT_CONVENTION.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ ├── docs.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md ├── actions │ ├── bump-lockfile │ │ └── action.yml │ ├── create-release-commit │ │ └── action.yml │ ├── publish-npm │ │ └── action.yml │ ├── publish-vs-marketplace │ │ └── action.yml │ └── run-tests │ │ └── action.yml ├── dependabot.yml ├── logo.svg └── workflows │ ├── bumpfile.yml │ ├── docs.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .release-please-manifest.json ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── docs ├── .vitepress │ ├── config.ts │ ├── grammar.ts │ └── theme │ │ ├── css │ │ ├── base.css │ │ ├── button.css │ │ ├── img.css │ │ ├── links.css │ │ └── variables.css │ │ └── index.ts ├── api │ ├── index.md │ ├── minze-element.md │ ├── minze.md │ └── type-helpers.md ├── guide │ ├── advanced │ │ ├── debugging.md │ │ ├── element-examples.md │ │ ├── patterns.md │ │ ├── performance.md │ │ ├── style-guide.md │ │ ├── syntax-highlighting.md │ │ ├── transitions.md │ │ └── typescript.md │ ├── components │ │ ├── data.md │ │ ├── events.md │ │ ├── hooks.md │ │ ├── methods.md │ │ ├── options.md │ │ ├── registration.md │ │ ├── selectors.md │ │ ├── styling.md │ │ ├── templating.md │ │ └── watchers.md │ ├── index.md │ ├── installation.md │ ├── introduction.md │ ├── minze │ │ ├── events.md │ │ └── registration.md │ └── publishing.md ├── index.md ├── integrations │ ├── ai │ │ ├── gradio.md │ │ ├── huggingface.md │ │ └── tensorflow.md │ ├── dev │ │ ├── eslint.md │ │ ├── playwright.md │ │ ├── prettier.md │ │ ├── storybook.md │ │ ├── unocss.md │ │ ├── vite.md │ │ └── vitest.md │ └── index.md └── public │ ├── CNAME │ ├── favicon.png │ ├── favicon.svg │ ├── hooks-dark.svg │ ├── hooks.svg │ ├── logo.png │ ├── logo.svg │ └── social.jpg ├── eslint.config.js ├── package-lock.json ├── package.json ├── packages ├── create-minze │ ├── .bumpfile │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── index.js │ ├── package.json │ ├── src │ │ ├── index.test.ts │ │ └── index.ts │ ├── template-storybook-ts │ │ ├── .storybook │ │ │ ├── global.css │ │ │ ├── main.ts │ │ │ └── preview.ts │ │ ├── _gitignore │ │ ├── package.json │ │ ├── src │ │ │ ├── lib │ │ │ │ ├── Introduction.mdx │ │ │ │ ├── logos │ │ │ │ │ ├── minze-logo.stories.ts │ │ │ │ │ ├── minze-logo.ts │ │ │ │ │ ├── storybook-logo.stories.ts │ │ │ │ │ └── storybook-logo.ts │ │ │ │ ├── my-button.stories.ts │ │ │ │ ├── my-button.ts │ │ │ │ ├── my-element.stories.ts │ │ │ │ └── my-element.ts │ │ │ ├── main.ts │ │ │ └── types │ │ │ │ └── vite-env.d.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── template-storybook │ │ ├── .storybook │ │ │ ├── global.css │ │ │ ├── main.js │ │ │ └── preview.js │ │ ├── _gitignore │ │ ├── jsconfig.json │ │ ├── package.json │ │ ├── src │ │ │ ├── lib │ │ │ │ ├── Introduction.mdx │ │ │ │ ├── logos │ │ │ │ │ ├── minze-logo.js │ │ │ │ │ ├── minze-logo.stories.js │ │ │ │ │ ├── storybook-logo.js │ │ │ │ │ └── storybook-logo.stories.js │ │ │ │ ├── my-button.js │ │ │ │ ├── my-button.stories.js │ │ │ │ ├── my-element.js │ │ │ │ └── my-element.stories.js │ │ │ └── main.js │ │ └── vite.config.js │ ├── template-vite-ts │ │ ├── _gitignore │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ ├── icon.svg │ │ │ └── index.html │ │ ├── src │ │ │ ├── assets │ │ │ │ └── vite.css │ │ │ ├── lib │ │ │ │ ├── logos │ │ │ │ │ ├── minze-logo.ts │ │ │ │ │ └── vite-logo.ts │ │ │ │ ├── my-button.ts │ │ │ │ └── my-element.ts │ │ │ ├── main.ts │ │ │ ├── preview.html │ │ │ ├── types │ │ │ │ └── vite-env.d.ts │ │ │ └── vite.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── template-vite │ │ ├── _gitignore │ │ ├── index.html │ │ ├── jsconfig.json │ │ ├── package.json │ │ ├── public │ │ │ ├── icon.svg │ │ │ └── index.html │ │ ├── src │ │ │ ├── assets │ │ │ │ └── vite.css │ │ │ ├── lib │ │ │ │ ├── logos │ │ │ │ │ ├── minze-logo.js │ │ │ │ │ └── vite-logo.js │ │ │ │ ├── my-button.js │ │ │ │ └── my-element.js │ │ │ ├── main.js │ │ │ ├── preview.html │ │ │ └── vite.js │ │ └── vite.config.js │ └── tsconfig.json ├── minze-vsc │ ├── .bumpfile │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── images │ │ └── logo.png │ ├── package.json │ ├── snippets │ │ ├── common.code-snippets │ │ ├── javascript.json │ │ └── typescript.json │ ├── syntaxes │ │ └── minze.tmLanguage.json │ └── test │ │ └── minze-element.ts ├── minze │ ├── .bumpfile │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── index.html │ ├── package.json │ ├── playwright.config.ts │ ├── public │ │ ├── icon.svg │ │ └── index.html │ ├── src │ │ ├── env.d.ts │ │ ├── lib │ │ │ ├── minze-element.ts │ │ │ ├── minze.ts │ │ │ ├── patcher.ts │ │ │ ├── test │ │ │ │ ├── minze-element.test.ts │ │ │ │ ├── minze.test.ts │ │ │ │ └── utils.test.ts │ │ │ └── utils.ts │ │ └── main.ts │ ├── test │ │ ├── assets │ │ │ └── vite.css │ │ ├── lib │ │ │ ├── template │ │ │ │ ├── logos │ │ │ │ │ ├── minze-logo.test.ts │ │ │ │ │ ├── minze-logo.ts │ │ │ │ │ ├── vite-logo.test.ts │ │ │ │ │ └── vite-logo.ts │ │ │ │ ├── my-button.test.ts │ │ │ │ ├── my-button.ts │ │ │ │ ├── my-element.test.ts │ │ │ │ └── my-element.ts │ │ │ └── testing │ │ │ │ ├── events │ │ │ │ ├── minze-event-listeners.test.ts │ │ │ │ ├── minze-event-listeners.ts │ │ │ │ ├── minze-on-events.test.ts │ │ │ │ └── minze-on-events.ts │ │ │ │ ├── minze-hooks.test.ts │ │ │ │ ├── minze-hooks.ts │ │ │ │ ├── minze-options.test.ts │ │ │ │ ├── minze-options.ts │ │ │ │ ├── reactivity │ │ │ │ ├── minze-attrs.test.ts │ │ │ │ ├── minze-attrs.ts │ │ │ │ ├── minze-patch.test.ts │ │ │ │ ├── minze-patch.ts │ │ │ │ ├── minze-reactive.test.ts │ │ │ │ ├── minze-reactive.ts │ │ │ │ ├── minze-watch.test.ts │ │ │ │ └── minze-watch.ts │ │ │ │ └── template │ │ │ │ ├── minze-exportparts.test.ts │ │ │ │ ├── minze-exportparts.ts │ │ │ │ ├── minze-selectors.test.ts │ │ │ │ ├── minze-selectors.ts │ │ │ │ ├── minze-templating.test.ts │ │ │ │ └── minze-templating.ts │ │ ├── main.ts │ │ ├── preview.html │ │ ├── types │ │ │ └── vite-env.d.ts │ │ ├── utils.ts │ │ └── vite.ts │ ├── tsconfig.json │ ├── vite.config.build.ts │ └── vite.config.ts └── vite-plugin-minze │ ├── .bumpfile │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── build.config.ts │ ├── package.json │ ├── src │ └── main.ts │ └── tsconfig.json └── release-please-config.json /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/vscode/devcontainers/typescript-node", 3 | "postCreateCommand": "npm install" 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/COMMIT_CONVENTION.md: -------------------------------------------------------------------------------- 1 | ## Git Commit Message Convention 2 | 3 | Your commit messages should follow the [Conventional Commits specification](https://www.conventionalcommits.org/). 4 | 5 | This repo uses [@sergejcodes/verify-commit-msg](https://www.npmjs.com/package/@sergejcodes/verify-commit-msg) to enforce a consistent commit message format. If your commit message does not follow the rules, you will receive an error. 6 | 7 | > You can use [commitlint.io](https://commitlint.io/) to help you generate a valid commit message. 8 | 9 | #### TL;DR: 10 | 11 | Messages must be matched by the [regex](https://www.npmjs.com/package/@sergejcodes/verify-commit-msg) enforced by `@sergejcodes/verify-commit-msg` package. 12 | 13 | #### Examples 14 | 15 | ``` 16 | feat(dev): add 'comments' option 17 | fix(dev): fix dev error 18 | perf(build)!: remove 'foo' option 19 | revert: feat(compiler): add 'comments' option 20 | ``` 21 | 22 | ### Revert 23 | 24 | If the PR reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit 25 | 26 | ### Scope 27 | 28 | The scope could be anything specifying the place of the commit change. For example `dev`, `build`, `workflow`, `cli` etc... 29 | 30 | ### Subject 31 | 32 | The subject contains a succinct description of the change: 33 | 34 | - use the imperative, present tense: "change" not "changed" nor "changes" 35 | - don't capitalize the first letter 36 | - no dot (.) at the end 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: "\U0001F41E Bug report" 2 | description: Report an issue with Minze 3 | labels: [pending triage] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this bug report! 9 | - type: textarea 10 | id: bug-description 11 | attributes: 12 | label: Describe the bug 13 | description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks! 14 | placeholder: I am doing ... What I expect is ... What actually happening is ... 15 | validations: 16 | required: true 17 | - type: input 18 | id: reproduction 19 | attributes: 20 | label: Reproduction 21 | description: Please provide a link via [StackBlitz](https://stackblitz.com) or a link to a repo that can reproduce the problem you ran into. `npm create minze@latest` can be used as a starter template. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required ([Why?](https://antfu.me/posts/why-reproductions-are-required)). If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "needs reproduction" label. 22 | placeholder: Reproduction URL 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: reproduction-steps 27 | attributes: 28 | label: Steps to reproduce 29 | description: Please provide any reproduction steps that may need to be described. 30 | placeholder: Run `npm install` followed by `npm run dev` 31 | - type: textarea 32 | id: system-info 33 | attributes: 34 | label: System Info 35 | description: Output of `npx envinfo --system --npmPackages '{minze,@minzejs/*}' --binaries --browsers` 36 | render: shell 37 | placeholder: System, Binaries, Browsers 38 | validations: 39 | required: true 40 | - type: dropdown 41 | id: package-manager 42 | attributes: 43 | label: Used Package Manager 44 | description: Select the used package manager 45 | options: 46 | - npm 47 | - yarn 48 | - pnpm 49 | validations: 50 | required: true 51 | - type: textarea 52 | id: logs 53 | attributes: 54 | label: Logs 55 | description: | 56 | Optional if provided reproduction. Please try not to insert an image but copy paste the log text. 57 | - type: checkboxes 58 | id: checkboxes 59 | attributes: 60 | label: Validations 61 | description: Before submitting the issue, please make sure you do the following 62 | options: 63 | - label: Follow our [Code of Conduct](https://github.com/sergejcodes/minze/blob/main/.github/CODE_OF_CONDUCT.md) 64 | required: true 65 | - label: Read the [Contributing Guidelines](https://github.com/sergejcodes/minze/blob/main/.github/CONTRIBUTING.md). 66 | required: true 67 | - label: Read the [docs](https://minze.dev/guide). 68 | required: true 69 | - label: Check that there isn't [already an issue](https://github.com/sergejcodes/minze/issues) that reports the same bug to avoid creating a duplicate. 70 | required: true 71 | - label: Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/sergejcodes/minze/discussions). 72 | required: true 73 | - label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug. 74 | required: true 75 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions & Discussions 4 | url: https://github.com/sergejcodes/minze/discussions 5 | about: Use GitHub discussions for message-board style questions and discussions 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/docs.yml: -------------------------------------------------------------------------------- 1 | name: "\U0001F4DA Documentation" 2 | description: Suggest a change or new page to be added to minze.dev 3 | labels: [documentation] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this issue! 9 | - type: checkboxes 10 | id: documentation_is 11 | attributes: 12 | label: Documentation is 13 | options: 14 | - label: Missing 15 | - label: Outdated 16 | - label: Confusing 17 | - label: With a typo 18 | - label: Not sure? 19 | - type: textarea 20 | id: description 21 | attributes: 22 | label: Explain in Detail 23 | description: A clear and concise description of your suggestion. If you intend to submit a PR for this issue, tell us in the description. Thanks! 24 | placeholder: The description of ... page is not clear. I thought it meant ... but it wasn't. 25 | validations: 26 | required: true 27 | - type: textarea 28 | id: suggestion 29 | attributes: 30 | label: Your Suggestion for Changes 31 | validations: 32 | required: true 33 | - type: input 34 | id: reproduction 35 | attributes: 36 | label: Reproduction 37 | description: If you have a reproduction, please provide a link via [StackBlitz](https://stackblitz.com) or a link to a repo that can reproduce the problem you ran into. `npm create minze@latest` can be used as a starter template. 38 | placeholder: Reproduction URL 39 | - type: textarea 40 | id: reproduction-steps 41 | attributes: 42 | label: Steps to reproduce 43 | description: Please provide any reproduction steps that may need to be described. 44 | placeholder: Run `npm install` followed by `npm run dev` 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "\U0001F680 New feature proposal" 2 | description: Propose a new feature to be added to Minze 3 | labels: ['enhancement: pending triage'] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for your interest in the project and taking the time to fill out this feature report! 9 | - type: textarea 10 | id: feature-description 11 | attributes: 12 | label: Description 13 | description: 'Clear and concise description of the problem. Please make the reason and usecases as detailed as possible. If you intend to submit a PR for this issue, tell us in the description. Thanks!' 14 | placeholder: As a developer using Minze I want [goal / wish] so that [benefit]. 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: suggested-solution 19 | attributes: 20 | label: Suggested solution 21 | description: 'In module [xy] we could provide following implementation...' 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: alternative 26 | attributes: 27 | label: Alternative 28 | description: Clear and concise description of any alternative solutions or features you've considered. 29 | - type: textarea 30 | id: additional-context 31 | attributes: 32 | label: Additional context 33 | description: Any other context or screenshots about the feature request here. 34 | - type: checkboxes 35 | id: checkboxes 36 | attributes: 37 | label: Validations 38 | description: Before submitting the issue, please make sure you do the following 39 | options: 40 | - label: Follow our [Code of Conduct](https://github.com/sergejcodes/minze/blob/main/.github/CODE_OF_CONDUCT.md) 41 | required: true 42 | - label: Read the [Contributing Guidelines](https://github.com/sergejcodes/minze/blob/main/.github/CONTRIBUTING.md). 43 | required: true 44 | - label: Read the [docs](https://minze.dev/guide). 45 | required: true 46 | - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. 47 | required: true 48 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Description 4 | 5 | 6 | 7 | ### Additional context 8 | 9 | 10 | 11 | --- 12 | 13 | ### What is the purpose of this pull request? 14 | 15 | - [ ] Bug fix 16 | - [ ] New feature 17 | - [ ] Documentation update 18 | - [ ] Other 19 | 20 | ### Before submitting the PR, please make sure you do the following 21 | 22 | - [ ] Read the [Contributing Guidelines](https://github.com/sergejcodes/minze/blob/main/.github/CONTRIBUTING.md). 23 | - [ ] Read the [Pull Request Guidelines](https://github.com/sergejcodes/minze/blob/main/.github/CONTRIBUTING.md#pull-request-guidelines) and follow the [PR Title Convention](https://github.com/sergejcodes/minze/blob/main/.github/COMMIT_CONVENTION.md). 24 | - [ ] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate. 25 | - [ ] Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `fixes #123`). 26 | - [ ] Ideally, include relevant tests that fail without this PR but pass with it. 27 | -------------------------------------------------------------------------------- /.github/actions/bump-lockfile/action.yml: -------------------------------------------------------------------------------- 1 | name: Bump Lockfile 2 | 3 | inputs: 4 | node-version: 5 | description: Node.js version 6 | 7 | runs: 8 | using: composite 9 | steps: 10 | - name: Set user 11 | shell: bash 12 | run: | 13 | git config --global user.name 'github-actions[bot]' 14 | git config --global user.email 'github-actions[bot]@users.noreply.github.com' 15 | 16 | - name: Use Node.js ${{ inputs.node-version }} 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: ${{ inputs.node-version }} 20 | 21 | - name: Install dependencies 22 | shell: bash 23 | run: npm install --ignore-scripts 24 | 25 | - name: Git add and commit 26 | shell: bash 27 | run: | 28 | git add package-lock.json 29 | git commit -m 'chore(deps): lock file bump' 30 | 31 | - name: Push to repo 32 | shell: bash 33 | run: git push origin main 34 | -------------------------------------------------------------------------------- /.github/actions/create-release-commit/action.yml: -------------------------------------------------------------------------------- 1 | name: Create Release Commit 2 | 3 | inputs: 4 | package-dir: 5 | description: Package directory 6 | required: true 7 | commit-msg: 8 | description: Commit Message 9 | required: false 10 | 11 | runs: 12 | using: composite 13 | steps: 14 | - name: Set user 15 | shell: bash 16 | run: | 17 | git config --global user.name 'github-actions[bot]' 18 | git config --global user.email 'github-actions[bot]@users.noreply.github.com' 19 | 20 | - name: Modify .bumpfile 21 | shell: bash 22 | run: echo $RANDOM > packages/${{ inputs.package-dir }}/.bumpfile 23 | 24 | - name: Git commit 25 | shell: bash 26 | run: | 27 | git commit -am "release: ${{ inputs.commit-msg || 'manual release' }}" 28 | 29 | - name: Push to repo 30 | shell: bash 31 | run: git push origin main 32 | -------------------------------------------------------------------------------- /.github/actions/publish-npm/action.yml: -------------------------------------------------------------------------------- 1 | name: Publish npm 2 | 3 | inputs: 4 | node-version: 5 | description: Node.js version 6 | package-name: 7 | description: Package name 8 | npm-token: 9 | description: NPM tocken 10 | 11 | runs: 12 | using: composite 13 | steps: 14 | - name: Use Node.js ${{ inputs.node-version }} 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: ${{ inputs.node-version }} 18 | registry-url: 'https://registry.npmjs.org' 19 | 20 | - name: Install dependencies 21 | shell: bash 22 | run: npm ci --ignore-scripts 23 | 24 | - name: Create builds 25 | shell: bash 26 | run: npm run build -w ${{ inputs.package-name }} 27 | 28 | - name: Publish ${{ inputs.package-name }} 29 | shell: bash 30 | run: npm publish -w ${{ inputs.package-name }} --ignore-scripts --access public 31 | env: 32 | NODE_AUTH_TOKEN: ${{ inputs.npm-token }} 33 | -------------------------------------------------------------------------------- /.github/actions/publish-vs-marketplace/action.yml: -------------------------------------------------------------------------------- 1 | name: Publish VS Marketplace 2 | 3 | inputs: 4 | node-version: 5 | description: Node.js version 6 | package-name: 7 | description: Package name 8 | vs-marketplace-token: 9 | description: VS Marketplace token 10 | 11 | runs: 12 | using: composite 13 | steps: 14 | - name: Use Node.js ${{ inputs.node-version }} 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: ${{ inputs.node-version }} 18 | registry-url: 'https://registry.npmjs.org' 19 | 20 | - name: Publish to Visual Studio Marketplace 21 | uses: HaaLeo/publish-vscode-extension@v2 22 | with: 23 | pat: ${{ inputs.vs-marketplace-token }} 24 | registryUrl: https://marketplace.visualstudio.com 25 | packagePath: ./packages/${{ inputs.package-name }} 26 | -------------------------------------------------------------------------------- /.github/actions/run-tests/action.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | inputs: 4 | node-version: 5 | description: Node.js version 6 | 7 | runs: 8 | using: composite 9 | steps: 10 | - name: Use Node.js ${{ inputs.node-version }} 11 | uses: actions/setup-node@v4 12 | with: 13 | node-version: ${{ inputs.node-version }} 14 | 15 | - name: Install dependencies 16 | shell: bash 17 | run: npm ci 18 | 19 | - name: Install Playwright browsers 20 | shell: bash 21 | run: npx playwright install --with-deps 22 | 23 | - name: Create builds 24 | shell: bash 25 | run: npm run build --workspaces --if-present # build all packages 26 | 27 | - name: Run package tests 28 | shell: bash 29 | run: npm test 30 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | groups: 8 | minor-and-patch: 9 | patterns: 10 | - '*' 11 | update-types: 12 | - minor 13 | - patch 14 | 15 | - package-ecosystem: npm 16 | directory: / 17 | schedule: 18 | interval: monthly 19 | groups: 20 | minor-and-patch: 21 | patterns: 22 | - '*' 23 | update-types: 24 | - minor 25 | - patch 26 | -------------------------------------------------------------------------------- /.github/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/bumpfile.yml: -------------------------------------------------------------------------------- 1 | # allows to trigger release-please manually by modifying the .bumpfile for selected package 2 | name: .bumpfile 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | package: 8 | type: choice 9 | description: 'Package to bump' 10 | required: true 11 | options: 12 | - minze 13 | - create-minze 14 | - minze-vsc 15 | - vite-plugin-minze 16 | commit-msg: 17 | type: string 18 | description: 'Commit message for release' 19 | 20 | env: 21 | PACKAGE_NAME: ${{ github.event.inputs.package }} 22 | PACKAGE_DIR: ${{ github.event.inputs.package }} 23 | COMMIT_MSG: ${{ github.event.inputs.commit-msg }} 24 | 25 | jobs: 26 | create-release-commit: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | 32 | - name: Create commit 33 | uses: ./.github/actions/create-release-commit 34 | with: 35 | package-dir: ${{ env.PACKAGE_DIR }} 36 | commit-msg: ${{ env.COMMIT_MSG }} 37 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | NODE_VERSION: 20 7 | 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup Node ${{ env.NODE_VERSION }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ env.NODE_VERSION }} 24 | cache: npm 25 | 26 | - name: Install 27 | run: npm ci 28 | 29 | - name: Build 30 | run: npm run docs-build 31 | 32 | - name: Upload Pages Artifact 33 | uses: actions/upload-pages-artifact@v3 34 | with: 35 | path: docs/.vitepress/dist/ 36 | 37 | deploy: 38 | needs: build 39 | runs-on: ubuntu-latest 40 | environment: 41 | name: github-pages 42 | url: ${{ steps.deployment.outputs.page_url }} 43 | steps: 44 | - name: Deploy to GitHub Pages 45 | id: deployment 46 | uses: actions/deploy-pages@v4 47 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'packages/**' 9 | - 'package.json' 10 | - '!**/README.md' 11 | workflow_run: 12 | workflows: 13 | - .bumpfile 14 | branches: 15 | - main 16 | types: 17 | - completed 18 | workflow_dispatch: 19 | 20 | env: 21 | NODE_VERSION: 20 22 | 23 | jobs: 24 | release-please: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Release Please 28 | uses: googleapis/release-please-action@v4 29 | id: release 30 | with: 31 | token: ${{ secrets.RELEASE_PLEASE_TOKEN }} 32 | outputs: 33 | releases-created: ${{ steps.release.outputs.releases_created }} 34 | package-name: ${{ ( 35 | (steps.release.outputs['packages/create-minze--release_created'] && 'create-minze') || 36 | (steps.release.outputs['packages/minze--release_created'] && 'minze') || 37 | (steps.release.outputs['packages/minze-vsc--release_created'] && 'minze-vsc') || 38 | (steps.release.outputs['packages/vite-plugin-minze--release_created'] && 'vite-plugin-minze') 39 | ) }} 40 | 41 | bump-lockfile: 42 | needs: 43 | - release-please 44 | runs-on: ubuntu-latest 45 | if: ${{ needs.release-please.outputs.releases-created == 'true' }} 46 | steps: 47 | - name: Checkout 48 | uses: actions/checkout@v4 49 | with: 50 | ref: main # needs to be explicitly set to pull the latest changes 51 | 52 | - name: Bump lock file 53 | uses: ./.github/actions/bump-lockfile 54 | with: 55 | node-version: ${{ env.NODE_VERSION }} 56 | 57 | publish-npm: 58 | needs: 59 | - release-please 60 | - bump-lockfile 61 | runs-on: ubuntu-latest 62 | if: ${{ needs.release-please.outputs.releases-created && needs.release-please.outputs.package-name != 'minze-vsc' }} 63 | steps: 64 | - name: Checkout 65 | uses: actions/checkout@v4 66 | with: 67 | ref: main # needs to be explicitly set to pull the latest changes 68 | 69 | - name: Publish to npm 70 | uses: ./.github/actions/publish-npm 71 | with: 72 | node-version: ${{ env.NODE_VERSION }} 73 | package-name: ${{ needs.release-please.outputs.package-name }} 74 | npm-token: ${{ secrets.NPM_TOKEN }} 75 | 76 | publish-vs-marketplace: 77 | needs: 78 | - release-please 79 | - bump-lockfile 80 | runs-on: ubuntu-latest 81 | if: ${{ needs.release-please.outputs.releases-created && needs.release-please.outputs.package-name == 'minze-vsc' }} 82 | steps: 83 | - name: Checkout 84 | uses: actions/checkout@v4 85 | with: 86 | ref: main # needs to be explicitly set to pull the latest changes 87 | 88 | - name: Publish to VS Marketplace 89 | uses: ./.github/actions/publish-vs-marketplace 90 | with: 91 | node-version: ${{ env.NODE_VERSION }} 92 | package-name: ${{ needs.release-please.outputs.package-name }} 93 | vs-marketplace-token: ${{ secrets.VS_MARKETPLACE_TOKEN }} 94 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'packages/**' 7 | - 'package.json' 8 | - '!packages/create-minze/template-**' 9 | - '!**/README.md' 10 | workflow_dispatch: 11 | 12 | env: 13 | NODE_VERSION: 20 14 | PACKAGE_NAME: ${{ github.event.inputs.package }} 15 | 16 | jobs: 17 | run-tests: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Run tests 24 | uses: ./.github/actions/run-tests 25 | with: 26 | node-version: ${{ env.NODE_VERSION }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *.log 4 | .*cache 5 | cache 6 | dist 7 | .idea 8 | *.timestamp* 9 | temp 10 | test-results 11 | coverage 12 | *.vsix 13 | *.dev.html 14 | storybook 15 | docs/.vitepress/syntaxes/*.json -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | package-lock.json 3 | .*cache 4 | cache 5 | *.d.ts 6 | *.mdx 7 | *.dev.html 8 | CHANGELOG.md 9 | storybook 10 | docs/.vitepress/syntaxes/*.json -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "singleQuote": true, 4 | "semi": false, 5 | "trailingComma": "none" 6 | } 7 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages/create-minze": "2.2.2", 3 | "packages/minze": "1.10.2", 4 | "packages/minze-vsc": "0.6.5", 5 | "packages/vite-plugin-minze": "1.1.6" 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "args": [ 9 | "--extensionDevelopmentPath=${workspaceFolder}/packages/minze-vsc" 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-present, Sergej Samsonenko 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /docs/.vitepress/grammar.ts: -------------------------------------------------------------------------------- 1 | import type { LanguageInput } from 'shiki' 2 | import { readFile } from 'node:fs/promises' 3 | import { fileURLToPath } from 'node:url' 4 | 5 | const filePath = '../../packages/minze-vsc/syntaxes/minze.tmLanguage.json' 6 | const grammar = JSON.parse( 7 | await readFile(fileURLToPath(new URL(filePath, import.meta.url)), 'utf-8') 8 | ) 9 | 10 | export const languages: LanguageInput[] = [ 11 | { 12 | injectTo: ['source.js', 'source.ts'], 13 | ...grammar 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/css/base.css: -------------------------------------------------------------------------------- 1 | html { 2 | overflow-y: scroll; 3 | } 4 | 5 | table td { 6 | vertical-align: baseline; 7 | } 8 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/css/button.css: -------------------------------------------------------------------------------- 1 | .VPButton.brand { 2 | transition: all 250ms; 3 | } 4 | 5 | .VPButton.brand:hover { 6 | box-shadow: 0 0 20px rgb(var(--minze-c-mint-1-values) / 50%); 7 | filter: hue-rotate(25deg); 8 | } 9 | 10 | .VPButton.brand:active { 11 | filter: hue-rotate(-10deg); 12 | } 13 | 14 | html:not(.dark) .VPButton.brand { 15 | background: linear-gradient( 16 | 45deg, 17 | var(--minze-c-teal-2) 0%, 18 | var(--minze-c-mint-2) 100% 19 | ); 20 | } 21 | 22 | html.dark .VPButton.brand { 23 | background: linear-gradient( 24 | 45deg, 25 | var(--minze-c-teal-1) 0%, 26 | var(--minze-c-mint-1) 100% 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/css/img.css: -------------------------------------------------------------------------------- 1 | html:not(.dark) .img-dark { 2 | display: none; 3 | } 4 | 5 | html.dark .img-light { 6 | display: none; 7 | } 8 | 9 | html:not(.dark) .VPHero img { 10 | filter: drop-shadow(0 0.75rem 1rem rgb(0 0 0 / 10%)); 11 | } 12 | 13 | html.dark .VPHero img { 14 | filter: drop-shadow(0 1rem 1rem rgb(0 0 0 / 20%)); 15 | } 16 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/css/links.css: -------------------------------------------------------------------------------- 1 | html:not(.dark) .VPSidebarItem.level-1:is(.is-active, .has-active) .text { 2 | font-weight: 600; 3 | } 4 | 5 | html:not(.dark) .VPSidebarItem.level-1.is-active .indicator, 6 | .outline-marker { 7 | background-color: var(--minze-c-mint-1) !important; 8 | } 9 | 10 | .VPFeature.link:hover { 11 | border-color: var(--minze-c-mint-1) !important; 12 | } 13 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/css/variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* teal */ 3 | --minze-c-teal-1-values: 80 220 200; 4 | --minze-c-teal-2-values: 50 250 220; 5 | --minze-c-teal-3-values: 160 255 240; 6 | 7 | --minze-c-teal-1: rgb(var(--minze-c-teal-1-values)); 8 | --minze-c-teal-2: rgb(var(--minze-c-teal-2-values)); 9 | --minze-c-teal-3: rgb(var(--minze-c-teal-3-values)); 10 | --minze-c-teal-soft: rgb(var(--minze-c-teal-1-values) / 14%); 11 | 12 | /* mint */ 13 | --minze-c-mint-1-values: 60 220 150; 14 | --minze-c-mint-2-values: 50 255 160; 15 | --minze-c-mint-3-values: 140 255 200; 16 | 17 | --minze-c-mint-1: rgb(var(--minze-c-mint-1-values)); 18 | --minze-c-mint-2: rgb(var(--minze-c-mint-2-values)); 19 | --minze-c-mint-3: rgb(var(--minze-c-mint-3-values)); 20 | --minze-c-mint-soft: rgb(var(--minze-c-mint-1-values) / 14%); 21 | 22 | /* brand */ 23 | --vp-c-brand-1: var(--vp-c-neutral); 24 | --vp-c-brand-2: var(--vp-c-text-2); 25 | --vp-c-brand-3: var(--minze-c-mint-2); 26 | --vp-c-brand-soft: var(--minze-c-mint-soft); 27 | 28 | /* button */ 29 | --vp-button-brand-text: var(--vp-c-black); 30 | --vp-button-brand-hover-text: var(--vp-c-black); 31 | --vp-button-brand-active-text: var(--vp-c-black); 32 | 33 | --vp-button-brand-bg: var(--minze-c-mint-2); 34 | --vp-button-brand-hover-bg: var(--minze-c-mint-2); 35 | 36 | --vp-button-brand-border: var(--minze-c-mint-3); 37 | --vp-button-brand-hover-border: var(--minze-c-mint-3); 38 | 39 | /* hero */ 40 | --vp-home-hero-name-color: transparent; 41 | --vp-home-hero-name-background: linear-gradient( 42 | 45deg, 43 | var(--minze-c-teal-1) 0%, 44 | var(--minze-c-mint-1) 60% 45 | ); 46 | 47 | --vp-home-hero-image-background-image: linear-gradient( 48 | -45deg, 49 | rgb(var(--minze-c-mint-1-values) / 100%) 50%, 50 | rgb(var(--minze-c-teal-1-values) / 100%) 50% 51 | ); 52 | --vp-home-hero-image-filter: blur(6rem); 53 | } 54 | 55 | :root.dark { 56 | /* brand */ 57 | --vp-c-brand-1: var(--minze-c-mint-1); 58 | --vp-c-brand-2: var(--minze-c-mint-2); 59 | --vp-c-brand-3: var(--minze-c-mint-3); 60 | --vp-c-brand-soft: var(--minze-c-mint-soft); 61 | 62 | /* button */ 63 | --vp-button-brand-border: var(--minze-c-mint-1); 64 | --vp-button-brand-hover-border: var(--minze-c-mint-1); 65 | 66 | /* hero */ 67 | --vp-home-hero-name-color: transparent; 68 | --vp-home-hero-name-background: linear-gradient( 69 | 45deg, 70 | var(--minze-c-teal-1) 0%, 71 | var(--minze-c-mint-3) 100% 72 | ); 73 | 74 | --vp-home-hero-image-background-image: linear-gradient( 75 | -45deg, 76 | rgb(var(--minze-c-mint-1-values) / 45%) 50%, 77 | rgb(var(--minze-c-teal-1-values) / 45%) 50% 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme' 2 | 3 | import './css/base.css' 4 | import './css/button.css' 5 | import './css/img.css' 6 | import './css/links.css' 7 | import './css/variables.css' 8 | 9 | export default DefaultTheme 10 | -------------------------------------------------------------------------------- /docs/api/index.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | All publicly available API properties, methods and types can be found under their respective sections. 4 | 5 | In this reference, the term `component` usually refers to the JavaScript instance of the web component, while the term `element` refers to the DOM representation of the web component, but both mean pretty much the same thing and are sometimes used interchangeably. 6 | 7 | | API | Description | 8 | | ------------------------------------ | ----------------------------------------- | 9 | | [`Minze`](/api/minze) | Global class with helpful static methods. | 10 | | [`MinzeElement`](/api/minze-element) | Base class for custom web components. | 11 | | [`Type Helpers`](/api/type-helpers) | TypeScript tuple Type Helpers. | 12 | -------------------------------------------------------------------------------- /docs/api/type-helpers.md: -------------------------------------------------------------------------------- 1 | # Type Helpers 2 | 3 | The properties `reactive`, `attrs`, `watch` and `eventListeners` are from the `tuple` type, but TypeScript doesn't automatically infer tuples and rather assumes that they are arrays. So you have to explicitly declare them as tuples. You can use the following types to do so: 4 | 5 | ## Reactive 6 | 7 | Declares `reactive` property as a mixed array of strings and tuples. 8 | 9 | ::: code-group 10 | 11 | ```ts [Code] 12 | import { MinzeElement, type Reactive } from 'minze' 13 | 14 | export interface MyElement { 15 | count: number 16 | } 17 | 18 | export class MyElement extends MinzeElement { 19 | reactive: Reactive = [['count', 0]] 20 | } 21 | ``` 22 | 23 | ```ts [Type] 24 | type Prop = string | [name: string, value: unknown, exposeAttr?: boolean] 25 | type Reactive = readonly Prop[] 26 | ``` 27 | 28 | ::: 29 | 30 | ## Attrs 31 | 32 | Declares `attrs` property as a mixed array of strings and tuples. 33 | 34 | ::: code-group 35 | 36 | ```ts [Code] 37 | import { MinzeElement, type Attrs } from 'minze' 38 | 39 | export interface MyElement { 40 | count: number 41 | } 42 | 43 | export class MyElement extends MinzeElement { 44 | attrs: Attrs = [['count', 0]] 45 | } 46 | ``` 47 | 48 | ```ts [Type] 49 | type Attr = string | [name: string, value?: unknown] 50 | type Attrs = readonly Attr[] 51 | ``` 52 | 53 | ::: 54 | 55 | ## Watch 56 | 57 | Declares `watch` property as an array of tuples. 58 | 59 | ::: code-group 60 | 61 | ```ts [Code] 62 | import { MinzeElement, type Watch } from 'minze' 63 | 64 | export interface MyElement { 65 | count: number 66 | } 67 | 68 | export class MyElement extends MinzeElement { 69 | watch: Watch = [ 70 | ['count', (newValue, oldValue) => console.log(newValue, oldValue)] 71 | ] 72 | } 73 | ``` 74 | 75 | ```ts [Type] 76 | type Callback = ( 77 | newValue?: any, 78 | oldValue?: any, 79 | key?: string, 80 | target?: object | typeof MinzeElement 81 | ) => Promise | void 82 | 83 | type Watch = readonly [name: string, callback: Callback][] 84 | ``` 85 | 86 | ::: 87 | 88 | ## EventListeners 89 | 90 | Declares `eventListeners` property as an array of tuples. 91 | 92 | ::: code-group 93 | 94 | ```ts [Code] 95 | import { MinzeElement, type EventListeners } from 'minze' 96 | 97 | export class MyElement extends MinzeElement { 98 | html = () => `` 99 | 100 | handleClick = (event: Event) => {} 101 | 102 | eventListeners: EventListeners = [['button', 'click', this.handleClick]] 103 | } 104 | ``` 105 | 106 | ```ts [Type] 107 | type EventListener = [ 108 | eventTarget: string | MinzeElement | typeof Window | BroadcastChannel, 109 | eventName: string, 110 | callback: (event: any) => void 111 | ] 112 | 113 | type EventListeners = readonly EventListener[] 114 | ``` 115 | -------------------------------------------------------------------------------- /docs/guide/advanced/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | You can debug a MinzeElement in several different ways. 4 | 5 | ## debug 6 | 7 | The component logs useful information about itself to the console when the `debug` property inside the component is set to `true`. The logging happens after the [`onReady`](/guide/components/hooks#lifecycle) hook. 8 | 9 | ```js 10 | import { MinzeElement } from 'minze' 11 | 12 | class MyElement extends MinzeElement { 13 | debug = true // [!code ++] 14 | } 15 | 16 | MyElement.define() 17 | ``` 18 | 19 | ```html 20 | 21 | ``` 22 | 23 | ## console 24 | 25 | You can use `console.log()` or `console.dir()` anywhere inside the component to log to the console. 26 | 27 | ```js 28 | import { MinzeElement } from 'minze' 29 | 30 | class MyElement extends MinzeElement { 31 | key = 'value' 32 | 33 | onReady() { 34 | console.log(this.key) // [!code ++] 35 | } 36 | } 37 | 38 | MyElement.define() 39 | ``` 40 | 41 | ```html 42 | 43 | ``` 44 | 45 | ## debugger 46 | 47 | The `debugger` statement, invokes any available debugging functionality. 48 | 49 | ::: warning 50 | The Browser DevTools need to be open for this to work. 51 | ::: 52 | 53 | ```js 54 | import { MinzeElement } from 'minze' 55 | 56 | class MyElement extends MinzeElement { 57 | onStart() { 58 | let value = 1 59 | debugger // [!code ++] 60 | value++ 61 | } 62 | } 63 | 64 | MyElement.define() 65 | ``` 66 | 67 | ```html 68 | 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/guide/advanced/element-examples.md: -------------------------------------------------------------------------------- 1 | # Element Examples 2 | 3 | Below you can find some advanced examples on how to create certain element functionality with Minze. 4 | 5 | ::: tip 6 | Check out the [Creating Native Web Components](https://tympanus.net/codrops/2022/03/04/creating-native-web-components/) article on codrops for a tutorial with practical examples about how to create native web components with Minze. 7 | ::: 8 | 9 | ## Link 10 | 11 | Recreating some basic functionality of the `` link element with Minze. 12 | 13 | ```js 14 | import { MinzeElement } from 'minze' 15 | 16 | class CustomLink extends MinzeElement { 17 | attrs = ['href', 'target'] 18 | static observedAttributes = ['href', 'target'] 19 | 20 | onStart() { 21 | this.role = 'link' 22 | this.tabIndex = 0 23 | } 24 | 25 | css = () => ` 26 | :host { 27 | display: inline-block; 28 | } 29 | 30 | :host(:hover) { 31 | cursor: pointer; 32 | text-decoration: underline; 33 | } 34 | ` 35 | 36 | open = (event) => { 37 | const keys = ['Enter', 'Spacebar', ' '] 38 | 39 | if ((this.href && event.type === 'click') || keys.includes(event.key)) { 40 | window.open(this.href, this.target ?? '_self') 41 | } 42 | } 43 | 44 | eventListeners = [ 45 | [this, 'click', this.open], 46 | [this, 'keydown', this.open] 47 | ] 48 | } 49 | 50 | CustomLink.define() 51 | ``` 52 | 53 | 54 | 55 | ```html 56 | 57 | Hello Minze! 58 | 59 | ``` 60 | 61 | 62 | 63 | ## Persistent Storage 64 | 65 | Implementing persistent storage inside a Minze Element with Local Storage. 66 | 67 | ```js 68 | import { MinzeElement } from 'minze' 69 | 70 | class MyElement extends MinzeElement { 71 | reactive = [['count', 0]] 72 | 73 | increaseCount = () => this.count++ 74 | 75 | watch = [ 76 | [ 77 | 'count', 78 | (newValue) => { 79 | localStorage.setItem('count', String(newValue)) 80 | } 81 | ] 82 | ] 83 | 84 | html = () => ` 85 | 88 | ` 89 | 90 | onReactive() { 91 | const count = localStorage.getItem('count') 92 | if (count) this.count = Number(count) 93 | } 94 | } 95 | 96 | MyElement.define() 97 | ``` 98 | 99 | ```html 100 | 101 | ``` 102 | -------------------------------------------------------------------------------- /docs/guide/advanced/performance.md: -------------------------------------------------------------------------------- 1 | # Performance 2 | 3 | ## Reactivity 4 | 5 | If you want to create performant web components, you have to use reactivity selectively. It doesn't mean you should ditch it altogether, instead, it's good to know when to use it and when not. 6 | 7 | Consider the following example: 8 | 9 | ```js 10 | import { MinzeElement } from 'minze' 11 | 12 | const nestedArray = [] 13 | 14 | for (let i = 0; i < 100; i++) { 15 | nestedArray.push({ num: i }) 16 | } 17 | 18 | class MyElement extends MinzeElement { 19 | reactive = [['nestedArray', nestedArray]] 20 | 21 | html = () => `${this.nestedArray[0].num}` 22 | 23 | onReady() { 24 | for (let i = 0; i < 100; i++) { 25 | this.nestedArray[0].num = i 26 | } 27 | } 28 | } 29 | 30 | MyElement.define() 31 | ``` 32 | 33 | ```html 34 | 35 | 36 | 37 | 38 | 39 | ``` 40 | 41 | **What's happening here?** 42 | 43 | We created a huge array of objects and made it deeply reactive. Then we created a loop in the `onReady` hook and reassigned one of the object's properties a bunch of times. Finally, we are rendering a template that displays the reassigned value. This seems simple but there's a lot going on under the hood: 44 | 45 | 1. Minze had to iterate over every property inside the `nestedArray` and make them all reactive. That happened `100` times for each used element. Since `my-element` was used 5 times the total is `500`. 46 | 2. The template has been rerendered `100` times because it includes a reactive property. Again since `my-element` was used 5 times the total is `500`. 47 | 48 | In total, our elements performed about `1000` steps that are not necessary for the end result. 49 | 50 | **A much better approach** 51 | 52 | ```js 53 | import { MinzeElement } from 'minze' 54 | 55 | const nestedArray = [] 56 | 57 | for (let i = 0; i < 100; i++) { 58 | nestedArray.push({ num: i }) 59 | } 60 | 61 | class MyElement extends MinzeElement { 62 | reactive = [['nestedArray', nestedArray]] // [!code --] 63 | nestedArray = nestedArray // [!code ++] 64 | 65 | html = () => `${this.nestedArray[0].num}` 66 | 67 | onReady() { 68 | for (let i = 0; i < 100; i++) { 69 | this.nestedArray[0].num = i 70 | } 71 | 72 | this.rerender() // [!code ++] 73 | } 74 | } 75 | 76 | MyElement.define() 77 | ``` 78 | 79 | ```html 80 | 81 | 82 | 83 | 84 | 85 | ``` 86 | 87 | Here we are not defining any reactive properties at all and instead, we are `rerendering` the component manually once our reassignment loop inside the `onReady` hook is done. 88 | 89 | Using this approach our total count for all under the hood steps is `10` for all 5 elements together. Each component is initially rendered `1` time and at the end, it's rerendered again. 90 | 91 | This can be further optimized by using the `onStart` hook instead of the `onReady` hook, which is executed before the first render. 92 | -------------------------------------------------------------------------------- /docs/guide/advanced/syntax-highlighting.md: -------------------------------------------------------------------------------- 1 | # Syntax Highlighting 2 | 3 | Once your HTML and CSS templates grow in size you might want to add some syntax highlighting. 4 | 5 | ## VS Code 6 | 7 | ### [Minze VS Code Extension](https://marketplace.visualstudio.com/items?itemName=sergejcodes.minze-vsc) 8 | 9 | The Minze VS Code extension comes bundled with snippets for auto-completion. It also adds auto-highlighting to class methods defined as arrow functions starting with `html` and `css` keyword. Additionally you can manually prefix any template literals with `/*html*/` to highlight HTML code inside them, or `/*css*/` to highlight CSS code. 10 | 11 | ```js 12 | import { MinzeElement } from Minze 13 | 14 | class MyElement extends MinzeElement { 15 | // auto 16 | html = () => `
` 17 | css = () => `:host {}` 18 | 19 | // auto 20 | htmlTemplate = () => `
` 21 | cssTemplate = () => `:host {}` 22 | 23 | // manual 24 | htmlCode = /*html*/ `
` 25 | cssCode = /*css*/ `:host {}` 26 | } 27 | ``` 28 | 29 | ### 3rd-Party Extensions 30 | 31 | With the following extension you can also add syntax highlighting to template strings: 32 | 33 | - [es6-string-html](https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html) 34 | - [Comment tagged templates](https://marketplace.visualstudio.com/items?itemName=bierner.comment-tagged-templates) 35 | - [Template Literals](https://marketplace.visualstudio.com/items?itemName=julienetie.vscode-template-literals) 36 | 37 | ```js 38 | import { MinzeElement } from 'minze' 39 | 40 | class MyElement extends MinzeElement { 41 | html = () => /*html*/ `
Hello Minze!
` 42 | css = () => /*css*/ `:host { display: flex; }` 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/guide/advanced/transitions.md: -------------------------------------------------------------------------------- 1 | # Transitions 2 | 3 | You can determine how components are animated when they are rendered for the first time or between state changes. 4 | 5 | ## Entry Transitions 6 | 7 | Entry transitions run when an element is rendered for the very first time. 8 | 9 | ### Local 10 | 11 | Animations can be defined inside of component. 12 | 13 | ```js 14 | import { MinzeElement } from 'minze' 15 | 16 | class MyElement extends MinzeElement { 17 | html = () => `
Hello Minze!
` 18 | 19 | css = () => ` 20 | :host { 21 | animation: rendered 0.25s ease-in; 22 | } 23 | 24 | @keyframes rendered { 25 | from { 26 | opacity: 0%; 27 | transform: translateY(100%); 28 | } 29 | to { 30 | opacity: 100%; 31 | transform: translateY(0); 32 | } 33 | } 34 | ` 35 | } 36 | 37 | MyElement.define() 38 | ``` 39 | 40 | ```html 41 | 42 | ``` 43 | 44 | ### Global 45 | 46 | By exposing the `rendered` attribute you can add animations to all rendered components, or define more specific rules. 47 | 48 | ::: code-group 49 | 50 | ```js 51 | import { MinzeElement } from 'minze' 52 | 53 | class MyElement extends MinzeElement { 54 | options = { exposeAttrs: { rendered: true } } 55 | 56 | html = () => `
Hello Minze!
` 57 | } 58 | 59 | MyElement.define() 60 | ``` 61 | 62 | ```css 63 | /* hide all custom web components until they are defined */ 64 | :not(:defined) { 65 | visibility: hidden; 66 | } 67 | 68 | [rendered] { 69 | animation: rendered 0.25s ease-in; 70 | } 71 | 72 | @keyframes rendered { 73 | 0% { 74 | opacity: 0%; 75 | transform: translateY(100%); 76 | } 77 | 100% { 78 | opacity: 100%; 79 | transform: translateY(0); 80 | } 81 | } 82 | ``` 83 | 84 | ::: 85 | 86 | ```html 87 | 88 | ``` 89 | 90 | ## View Transitions 91 | 92 | You can activate View Transitions for a specific element by setting the `viewTransitions` option. Minze then leverages the [View Transitions API](https://developer.mozilla.org/docs/Web/API/View_Transitions_API) when the template changes. Currently this only works with supported browsers, see [Can I use](https://caniuse.com/?search=View%20Transition%20API) for more info. 93 | 94 | ::: tip 95 | By default, View Transitions API animates CSS `opacity`. Read more about customizing View Transitions with CSS on [developer.chrome.com](https://developer.chrome.com/docs/web-platform/view-transitions/). 96 | ::: 97 | 98 | ```js 99 | import { MinzeElement } from 'minze' 100 | 101 | class MyElement extends MinzeElement { 102 | options = { viewTransitions: true } 103 | 104 | reactive = [['active', false]] 105 | 106 | toggle = () => (this.active = !this.active) 107 | 108 | html = () => ` 109 | 112 | ` 113 | } 114 | 115 | MyElement.define() 116 | ``` 117 | 118 | ```html 119 | 120 | ``` 121 | -------------------------------------------------------------------------------- /docs/guide/advanced/typescript.md: -------------------------------------------------------------------------------- 1 | # TypeScript 2 | 3 | This section assumes you already have a basic understanding of [TypeScript](https://www.typescriptlang.org). The process of writing Minze components in TypeScript is similar to writing them in vanilla JavaScript. 4 | 5 | There are two main differences: 6 | 7 | 1. The types for `reactive`, `attrs`, `watch` and `eventListeners` have to be explicitly declared. You can do so by using [Type Helpers](/api/type-helpers) provided with Minze. 8 | 2. Reactive properties and attributes are created dynamically so you have to explicitly declare their types in a separate `interface` named after the component and export it. 9 | 10 | ::: warning 11 | If you are using [typescript-eslint](https://typescript-eslint.io) with Minze, you have to disable the `@typescript-eslint/no-unsafe-declaration-merging` rule. 12 | ::: 13 | 14 | ::: code-group 15 | 16 | 17 | ```ts [Reactive] 18 | import type { Reactive } from 'minze' // [!code focus] 19 | import { MinzeElement } from 'minze' 20 | 21 | export interface MyElement { // [!code focus] 22 | aBoolean: boolean // [!code focus] 23 | anObject: { foo: string } // [!code focus] 24 | } // [!code focus] 25 | 26 | export class MyElement extends MinzeElement { 27 | reactive: Reactive = [ // [!code focus] 28 | ['aBoolean', false], 29 | ['anObject', { foo: 'bar' }] 30 | ] 31 | } 32 | ``` 33 | 34 | 35 | 36 | ```ts [Attrs] 37 | import type { Attrs } from 'minze' // [!code focus] 38 | import { MinzeElement } from 'minze' 39 | 40 | export interface MyElement { // [!code focus] 41 | text: string | null // [!code focus] 42 | bgColor: string // [!code focus] 43 | config: Record // [!code focus] 44 | } // [!code focus] 45 | 46 | export class MyElement extends MinzeElement { 47 | attrs: Attrs = [ // [!code focus] 48 | 'text', 49 | ['bg-color', '#000'], 50 | ['config', { key1: 'value', key2: 'value' }] 51 | ] 52 | 53 | static observedAttributes = ['text', 'bg-color', 'config'] 54 | } 55 | ``` 56 | 57 | 58 | ```ts [Watch] 59 | import type { Watch } from 'minze' // [!code focus] 60 | import { MinzeElement, type Watch } from 'minze' 61 | 62 | export class MyElement extends MinzeElement { 63 | watch: Watch = [['aBoolean', () => {}]] // [!code focus] 64 | } 65 | ``` 66 | 67 | 68 | ```ts [EventListeners] 69 | import type { EventListeners } from 'minze' // [!code focus] 70 | import { MinzeElement } from 'minze' 71 | 72 | export class MyElement extends MinzeElement { 73 | html = () => `` 74 | 75 | handleClick = (event: Event) => { 76 | console.log(event.target) // 77 | } 78 | 79 | eventListeners: EventListeners = [ // [!code focus] 80 | ['.button', 'click', this.handleClick] 81 | ] 82 | } 83 | ``` 84 | 85 | 86 | ```ts [./main.ts] 87 | import { MyElement } from './my-element' 88 | MyElement.define() 89 | ``` 90 | 91 | ::: 92 | 93 | ```html 94 | 95 | ``` 96 | -------------------------------------------------------------------------------- /docs/guide/components/hooks.md: -------------------------------------------------------------------------------- 1 | # Hooks 2 | 3 | Hooks are methods that can be defined within a component and are called at various points in the lifecycle of a component. Some hooks run only once during the component lifecycle, while others can re-run under certain circumstances. All hooks can be asynchronous. See the [API reference](/api/minze-element#hooks) for more details. 4 | 5 | ::: code-group 6 | 7 | ```js [sync] 8 | import { MinzeElement } from 'minze' 9 | 10 | class MyElement extends MinzeElement { 11 | onReady() { 12 | console.log('onReady') // 'onReady' 13 | } 14 | } 15 | 16 | MyElement.define() 17 | ``` 18 | 19 | ```js [async] 20 | import { MinzeElement } from 'minze' 21 | 22 | class MyElement extends MinzeElement { 23 | async onReady() { 24 | console.log('onReady') // 'onReady' 25 | } 26 | } 27 | 28 | MyElement.define() 29 | ``` 30 | 31 | ::: 32 | 33 | ```html 34 | 35 | ``` 36 | 37 | ## Overview 38 | 39 | This overview should help you to decide which of the available hooks to choose for a particular situation. 40 | 41 | - **Order:** Execution order of the hook. 42 | - **Common:** This hook always runs during a component lifecycle. Uncommon hooks run under specific circumstances. E.g. `onDestroy` hook runs only when the element is removed from the DOM. 43 | - **Re-runs:** Hook can run more than once during a component lifecycle, before the element is moved or destroyed. 44 | - **Reactive:** If reactive properties can be accessed within the hook. 45 | - **Template:** Hook has access to the **rendered** template when it runs. 46 | 47 | `✅ Yes` `❌ No` `❔ depends on execution context` 48 | 49 | | Hook | Order | Common | Re-runs | Reactive | Template | 50 | | ------------------------------------------------------------------- | ----- | ------ | ------- | -------- | -------- | 51 | | [`onStart`](/api/minze-element#onstart) | `1` | ✅ | ❌ | ❌ | ❌ | 52 | | [`onReactive`](/api/minze-element#onreactive) | `2` | ✅ | ❌ | ✅ | ❌ | 53 | | [`beforeRender`](/api/minze-element#beforerender) | `3` | ✅ | ✅ | ✅ | ❌ | 54 | | [`afterRender`](/api/minze-element#afterrender) | `4` | ✅ | ✅ | ✅ | ✅ | 55 | | [`onReady`](/api/minze-element#onready) | `5` | ✅ | ❌ | ✅ | ✅ | 56 | | [`onDestroy`](/api/minze-element#ondestroy) | ❔ | ❌ | ❌ | ✅ | ✅ | 57 | | [`onMove`](/api/minze-element#onmove) | ❔ | ❌ | ❌ | ✅ | ✅ | 58 | | [`beforeAttributeChange`](/api/minze-element#beforeattributechange) | ❔ | ❌ | ✅ | ❔ | ❔ | 59 | | [`afterAttributeChange`](/api/minze-element#afterattributechange) | ❔ | ❌ | ✅ | ❔ | ❔ | 60 | 61 | ## Lifecycle 62 | 63 |

64 | 65 | 66 |

67 | -------------------------------------------------------------------------------- /docs/guide/components/methods.md: -------------------------------------------------------------------------------- 1 | # Methods 2 | 3 | You can define methods on the component to extend its functionality. 4 | 5 | ::: tip 6 | Certain keywords are reserved for special functionality and shouldn't be overwritten. E.g. `reactive`, `attrs`, etc. See the [API reference](/api/minze-element) for a comprehensive list. 7 | ::: 8 | 9 | ```js 10 | import { MinzeElement } from 'minze' 11 | 12 | class MyElement extends MinzeElement { 13 | firstMethod() { 14 | console.log('Hello Minze!') 15 | } 16 | 17 | secondMethod = () => { 18 | console.log('Hello Minze again!') 19 | } 20 | 21 | onReady = () => { 22 | this.firstMethod() // 'Hello Minze!' 23 | this.secondMethod() // 'Hello Minze again!' 24 | } 25 | } 26 | 27 | MyElement.define() 28 | ``` 29 | 30 | ```html 31 | 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/guide/components/options.md: -------------------------------------------------------------------------------- 1 | # Options 2 | 3 | Individual components can be customized by declaring an options property. See all currently available options in the [API reference](/api/minze-element#options). 4 | 5 | ```js 6 | import { MinzeElement } from 'minze' 7 | 8 | class MyElement extends MinzeElement { 9 | options = { 10 | // ... 11 | } 12 | } 13 | 14 | MyElement.define() 15 | ``` 16 | 17 | ```html 18 | 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/guide/components/registration.md: -------------------------------------------------------------------------------- 1 | # Registration 2 | 3 | The quickest way to register a component is to use the `define` method of the respective component. 4 | 5 | ::: tip 6 | Alternatively, you can register multiple components at once by using the `defineAll` method of the `Minze` class. See the [API reference](/api/minze#defineall) for more information. 7 | ::: 8 | 9 | ::: warning 10 | Your component class name should be in `PascalCase` when using this registration method. 11 | ::: 12 | 13 | ::: code-group 14 | 15 | ```js [Regular] 16 | import { MinzeElement } from 'minze' 17 | 18 | class MyElement extends MinzeElement { 19 | // ... 20 | } 21 | 22 | MyElement.define() // automatic naming based on the class name 23 | MyElement.define('my-custom-element') // manual naming 24 | ``` 25 | 26 | 27 | ```js [Shorthand] 28 | import { MinzeElement } from 'minze' 29 | 30 | ;(class MyElement extends MinzeElement { 31 | // ... 32 | }).define() 33 | ``` 34 | 35 | 36 | ::: 37 | 38 | 39 | ```html 40 | 41 | 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /docs/guide/components/selectors.md: -------------------------------------------------------------------------------- 1 | # Selectors 2 | 3 | ::: tip 4 | Selectors access the rendered component template, see [Hooks Overview](/guide/components/hooks#overview) to know during which hooks the template is already rendered. 5 | ::: 6 | 7 | ## select / selectAll 8 | 9 | Selects elements within the components `html` template (shadow DOM) by providing a valid `CSS` selector. 10 | 11 | - `select` - Selects the first element that matches the selector. 12 | - `selectAll` - Selects all elements that match the selector. 13 | 14 | ```js 15 | import { MinzeElement } from 'minze' 16 | 17 | class MyElement extends MinzeElement { 18 | html = () => ` 19 |
20 |
21 | ` 22 | 23 | onReady() { 24 | console.log(this.select('#my-div')) //
25 | console.log(this.selectAll('div')) // [div, div] 26 | } 27 | } 28 | 29 | MyElement.define() 30 | ``` 31 | 32 | ```html 33 | 34 | ``` 35 | 36 | ## slotted 37 | 38 | Returns an array of slotted element(s) for provided slot name or the `default` slot. 39 | 40 | ```js 41 | import { MinzeElement } from 'minze' 42 | 43 | class MyElement extends MinzeElement { 44 | html = () => ` 45 | 46 | 47 | ` 48 | 49 | onReady() { 50 | console.log(this.slotted('default')) // [h1, p] 51 | console.log(this.slotted('named-slot')) // [h2, p] 52 | } 53 | } 54 | 55 | MyElement.define() 56 | ``` 57 | 58 | ```html 59 | 60 |

Headline 1

61 |

Paragraph 1

62 | 63 |

Headline 2

64 |

Paragraph 2

65 |
66 | ``` 67 | -------------------------------------------------------------------------------- /docs/guide/components/watchers.md: -------------------------------------------------------------------------------- 1 | # Watchers 2 | 3 | `watch` watches the given reactive properties and calls the provided callback whenever a change is detected. `watch` should be an array containing one or more tuples. In JavaScript, tuples are ordinary arrays, but in TypeScript they are their own type, defining the length of the array and the types of its elements. 4 | 5 | Every tuple takes exactly 2 values. 6 | 7 | Tuple structure: [`name`, `callback`] 8 | 9 | 1. **name:** the reactive property name to watch. Has to be `camelCase`, can be optionally `dash-case` for reactive attributes declared in `attrs`. 10 | 2. **callback:** a callback function that runs whenever one of the property's values changes. Can be asynchronous. 11 | 12 | ::: warning 13 | `watch` only works with reactive properties that were defined with `reactive` or `attrs`. 14 | ::: 15 | 16 | ```js 17 | import { MinzeElement } from 'minze' 18 | 19 | class MyElement extends MinzeElement { 20 | reactive = [['count', 0]] 21 | 22 | watchCount = (newValue, oldValue, key, target) => { 23 | console.log(newValue, oldValue, key, target) // 1, 0, 'count', MyElement 24 | } 25 | 26 | watch = [['count', this.watchCount]] 27 | 28 | onReady() { 29 | this.count = 1 30 | } 31 | } 32 | 33 | MyElement.define() 34 | ``` 35 | 36 | ```html 37 | 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/guide/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | > Minze is a dead-simple JS framework for native web components. 4 | 5 | ## Overview 6 | 7 | Minze (German shorthand for "Peppermint", pronounced [`/ˈmɪnt͡sə/`](https://upload.wikimedia.org/wikipedia/commons/c/c2/De-Minze.ogg)) lets you rapidly build native web components. 8 | 9 | It is not a framework for building complex user interfaces or single-page apps, like React, Vue or Angular. Instead, you can use Minze to create `encapsulated`, `reusable`, `maintainable`, `cross-framework` native web components. 10 | 11 | Minze was made to be accessible to everyone, and to be easy to use. It removes a lot of complexity from the creation of custom web components. 12 | 13 | Possible use cases: 14 | 15 | - **Design Systems**: Create cross-framework design system libraries and share them with your team or the world. Define once, use everywhere. 16 | 17 | - **Light Alternative**: If you don't want to use a common framework, but still want to create some components with JavaScript functionality for your site, you can use Minze. 18 | 19 | - **Browser Native Extending**: You can add Minze to any web project and create components without using any build tools to extend its functionality. 20 | 21 | ## Features 22 | 23 | - 👶 Simple - Dive right in by [scaffolding a project](/guide/installation#cli), installing from [npm](/guide/installation#npm) or using [CDN link](/guide/installation#cdn). 24 | - ⚡ Fast - Tiny footprint ~3KB (minified and compressed). 25 | - 🚀 Modern - Based on the latest technologies around web components. 26 | - 📦 Shareable - Build component libraries or design systems. Define once, use everywhere. 27 | - 🎲 Framework Agnostic - Use Minze with any common framework - React, Vue, Svelte, etc ... 28 | - 📕 Storybook - Minze x Storybook dev environment integration. 29 | - 📖 Extensive Docs - Comprehensive documentation and API reference. 30 | - 🔒 Typed API - Scale your component library with ease by using TypeScript. 31 | 32 | ## Architecture 33 | 34 | Minze consists of two main classes: 35 | 36 | - **Minze** - A class with multiple static methods for common tasks, like defining several components at once. 37 | - **MinzeElement** - Base class for custom web components. It adds an abstraction layer around the web components API and several additional features like reactivity. 38 | 39 | There's also a [CLI tool](/guide/installation#cli) that can be used to create a development environment for Minze components. Out of the box, it runs with vanilla [Vite](https://vitejs.dev/) or [Storybook](https://storybook.js.org) and optionally `TypeScript`. 40 | 41 | ::: tip 42 | Minze and MinzeElement are based on JavaScript classes, if you need a basic refresher on JavaScript classes check the ["Using classes" mdn guide](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Using_classes) or the [Classes API](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Classes). 43 | ::: 44 | 45 | ## Browser Support 46 | 47 | By default, Minze is transpiled to `ES2020`. Minze should be compatible with all [browsers supporting ES2020](https://caniuse.com/?search=es2020) and [Custom Elements](https://caniuse.com/?search=Custom%20Elements). 48 | -------------------------------------------------------------------------------- /docs/guide/minze/events.md: -------------------------------------------------------------------------------- 1 | # Events 2 | 3 | Abstraction layer for custom events. Useful for listening to events dispatched from custom components. All event listeners created with `Minze` class will be attached to the `window` object. 4 | 5 | See the [API reference](/api/minze#events) for more information. 6 | 7 | ::: code-group 8 | 9 | ```js [./my-element.js] 10 | import { MinzeElement } from 'minze' 11 | 12 | class MyElement extends MinzeElement { 13 | onReady() { 14 | this.dispatch('minze:event', { msg: 'Hello Minze!' }) 15 | } 16 | } 17 | 18 | MyElement.define() 19 | ``` 20 | 21 | ```js [./some-other-file.js] 22 | import Minze from 'minze' 23 | 24 | const handleDispatch = (event) => { 25 | console.log(event.detail) // {msg: 'Hello Minze!'} 26 | Minze.stopListen('minze:event', handleDispatch) 27 | } 28 | 29 | Minze.listen('minze:event', handleDispatch) 30 | ``` 31 | 32 | ::: 33 | 34 | ```html 35 | 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/guide/minze/registration.md: -------------------------------------------------------------------------------- 1 | # Registration 2 | 3 | You can register components in two different ways: one by one or all at once. 4 | 5 | ## define 6 | 7 | Define a single component by providing a name and a component class. 8 | 9 | ::: tip 10 | Alternatively, you can define components by calling their respective `define` method. See the [API reference](/api/minze-element#define) for more information. 11 | ::: 12 | 13 | ::: tip 14 | You can also import the main `Minze` class as a default import:
`import Minze, { MinzeElement } from 'minze'` 15 | ::: 16 | 17 | ::: warning 18 | Custom component names should always consist of at least two words. 19 | ::: 20 | 21 | ```js 22 | import { Minze, MinzeElement } from 'minze' 23 | 24 | class MyElement extends MinzeElement { 25 | html = () => `
my element
` 26 | } 27 | 28 | Minze.define('my-element', MyElement) 29 | ``` 30 | 31 | ```html 32 | 33 | ``` 34 | 35 | ## defineAll 36 | 37 | Define multiple components at once. They will be automatically defined in `dash-case` format. The provided components can either be an array of Minze elements, a module object, or a module-map generated with tools like vite's `import.meta.glob` 38 | 39 | ::: warning 40 | Your component class names should be in `PascalCase` when using this registration method. 41 | ::: 42 | 43 | ::: code-group 44 | 45 | ```js [Array] 46 | import { Minze } from 'minze' 47 | import { MyFirstElement, MySecondElement } from './elements.js' 48 | 49 | Minze.defineAll([MyFirstElement, MySecondElement]) 50 | ``` 51 | 52 | ```js [Module] 53 | import { Minze } from 'minze' 54 | import * as elements from './elements.js' 55 | 56 | Minze.defineAll(elements) 57 | ``` 58 | 59 | 60 | ```js [Module-Map] 61 | import { Minze } from 'minze' 62 | const modules = { 63 | 'first-element': async () => (await import('./element.js')).FirstElement, 64 | 'second-element': async () => (await import('./element.js')).SecondElement, 65 | 'all': () => import('./element.js') 66 | } 67 | 68 | Minze.defineAll(modules) 69 | ``` 70 | 71 | 72 | ```js [Module-Map (Vite)] 73 | import { Minze } from 'minze' 74 | const modules = import.meta.glob('./lib/**/*.@(ts|js)') 75 | 76 | Minze.defineAll(modules) 77 | ``` 78 | 79 | ```js [./elements.js] 80 | import { MinzeElement } from 'minze' 81 | 82 | export class FirstElement extends MinzeElement {} 83 | export class SecondElement extends MinzeElement {} 84 | ``` 85 | 86 | ::: 87 | 88 | 89 | ```html 90 | 91 | 92 | ``` 93 | 94 | 95 | ::: tip 96 | If you are using the module-map registration method, you can specify which components should be registered by providing an array of keys as the second argument. E.g. `Minze.defineAll(modules, ['first-element'])` 97 | ::: 98 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | title: JS framework for native web components 5 | description: Minze lets you rapidly build native web components. Create encapsulated, reusable, cross-framework web components and scale your component library with ease. 6 | 7 | hero: 8 | name: Minze 9 | text: Web components Dead-simple 10 | tagline: JS framework for native web components. 11 | image: 12 | src: /logo.svg 13 | alt: Minze 14 | actions: 15 | - theme: brand 16 | text: Quick Start 17 | link: /guide/ 18 | - theme: alt 19 | text: Learn More 20 | link: /guide/introduction 21 | 22 | features: 23 | - icon: 👶 24 | title: Simple 25 | details: Dive right in by scaffolding a project, installing from npm or using a CDN link. 26 | link: /guide/ 27 | linkText: Quick Start 28 | - icon: ⚡ 29 | title: Fast 30 | details: Tiny footprint ~3KB (minified and compressed). 31 | link: https://www.cloudflare.com/learning/performance/why-site-speed-matters 32 | linkText: Why site speed matters 33 | - icon: 🚀 34 | title: Modern 35 | details: Based on the latest technologies around web components. 36 | link: https://developer.mozilla.org/docs/Web/API/Web_components 37 | linkText: Web Components on mdn 38 | - icon: 📦 39 | title: Shareable 40 | details: Build component libraries or design systems. Define once, use everywhere. 41 | link: /guide/publishing 42 | linkText: Publishing 43 | - icon: 🎲 44 | title: Framework Agnostic 45 | details: Use Minze with any common framework - React, Vue, Svelte, etc ... 46 | link: https://stackoverflow.com/questions/64725017/what-does-it-mean-by-framework-agnostic 47 | linkText: Framework Agnostic? 48 | - icon: 📕 49 | title: Storybook 50 | details: Minze x Storybook dev environment integration. 51 | link: /guide/installation#cli 52 | linkText: npm crate minze 53 | - icon: 📖 54 | title: Extensive Docs 55 | details: Comprehensive documentation and API reference. 56 | link: /guide/ 57 | linkText: Guide 58 | - icon: 🔒 59 | title: Typed API 60 | details: Scale your component library with ease by using TypeScript. 61 | link: /api/ 62 | linkText: API reference 63 | --- 64 | -------------------------------------------------------------------------------- /docs/integrations/ai/tensorflow.md: -------------------------------------------------------------------------------- 1 | # TensorFlow 2 | 3 | > TensorFlow is an end-to-end machine learning platform. 4 | 5 | ## [TensorFlow.js](https://www.tensorflow.org/js) 6 | 7 | TensorFlow.js is a JavaScript library for training, deploying and consuming machine learning models. With Minze and TensorFlow.js you can use Machiene Learning directly in your web components. 8 | 9 | > The following guide is based on a fresh Minze CLI installation. 10 | 11 | 1. Install dependencies. 12 | 13 | ::: code-group 14 | 15 | ```bash [npm] 16 | $ npm add @tensorflow/tfjs 17 | ``` 18 | 19 | ```bash [yarn] 20 | $ yarn add @tensorflow/tfjs 21 | ``` 22 | 23 | ```bash [pnpm] 24 | $ pnpm add @tensorflow/tfjs 25 | ``` 26 | 27 | ```bash [bun] 28 | $ bun add @tensorflow/tfjs 29 | ``` 30 | 31 | ::: 32 | 33 | 2. Create a `tf-element.js` file inside the `src/lib` directory. 34 | 35 | ``` 36 | src/ 37 | └─ lib/ 38 | ├─ ... 39 | └─ tf-element.js // [!code ++] 40 | ``` 41 | 42 | 3. Import `@tensorflow/tfjs` and define a component inside the new file. 43 | 44 | ::: code-group 45 | 46 | 47 | ```js [src/lib/tf-element.js] 48 | import { MinzeElement } from 'minze' 49 | import * as tf from '@tensorflow/tfjs' 50 | 51 | export class MyElement extends MinzeElement { 52 | reactive = [['pred', 'training ...']] 53 | 54 | html = () => `${this.pred}` 55 | 56 | async onReactive() { 57 | // simple linear regression (y = x * 2 + 0) 58 | 59 | // features and labels 60 | const x = tf.tidy(() => tf.range(1, 101, 1).reshape([-1, 1])) 61 | const y = x.mul(2).add(0) 62 | 63 | // create the model 64 | const model = tf.sequential() 65 | model.add(tf.layers.dense({ units: 1, inputShape: [1] })) 66 | 67 | // compile the model 68 | model.compile({ 69 | loss: 'meanSquaredError', 70 | optimizer: tf.train.sgd(0.0001) 71 | }) 72 | 73 | // train the model 74 | await model.fit(x, y, { epochs: 3 }) 75 | 76 | // make prediction (the result should be around 400) 77 | const xPred = tf.tensor([200], [1, 1]) 78 | const yPred = model.predict(xPred) 79 | 80 | // assign to reactive property 81 | this.pred = Array.isArray(yPred) ? yPred[0] : await yPred.array() 82 | 83 | // clean up 84 | tf.dispose([x, y, xPred, yPred]) 85 | tf.disposeVariables() 86 | } 87 | } 88 | ``` 89 | 90 | ::: 91 | 92 | 4. Add the new element to `src/preview.html` file. 93 | 94 | ::: code-group 95 | 96 | 97 | ```html [src/preview.html] 98 | // [!code ++] 99 | 100 |

Minze + Vite

101 |
102 | ``` 103 | 104 | 105 | ::: 106 | 107 | ::: tip 108 | For more details about TensorFlow.js refer to the [TensorFlow.js docs](https://www.tensorflow.org/js). 109 | ::: 110 | -------------------------------------------------------------------------------- /docs/integrations/dev/prettier.md: -------------------------------------------------------------------------------- 1 | # Prettier 2 | 3 | [Prettier](https://prettier.io) is an opinionated code formatter. 4 | 5 | If you used the [CLI method](/guide/installation#cli) to install Minze you can extend your environment with auto-code formatting quite quickly. 6 | 7 | > The following guide is based on a fresh Minze CLI installation. 8 | 9 | 1. Install dependencies. 10 | 11 | ::: code-group 12 | 13 | ```bash [npm] 14 | $ npm add -D prettier 15 | ``` 16 | 17 | ```bash [yarn] 18 | $ yarn add -D prettier 19 | ``` 20 | 21 | ```bash [pnpm] 22 | $ pnpm add -D prettier 23 | ``` 24 | 25 | ```bash [bun] 26 | $ bun add -D prettier 27 | ``` 28 | 29 | ::: 30 | 31 | 2. Add format script to `package.json`. 32 | 33 | ::: code-group 34 | 35 | ```json [package.json] 36 | { 37 | "scripts": { 38 | // ... 39 | "format": "prettier --write --cache ." 40 | } 41 | } 42 | ``` 43 | 44 | ::: 45 | 46 | 2. Create and populate `.prettierignore` and `.prettierrc.json` files. 47 | 48 | ::: code-group 49 | 50 | ```txt [files] 51 | ├─ src/ 52 | ├─ ... 53 | ├─ .prettierignore // [!code ++] 54 | └─ .prettierrc.json // [!code ++] 55 | ``` 56 | 57 | ```[.prettierignore] 58 | dist 59 | package-lock.json 60 | .*cache 61 | cache 62 | *.d.ts 63 | *.mdx 64 | *.dev.html 65 | storybook 66 | ``` 67 | 68 | ```json [.prettierrc.json] 69 | { 70 | "$schema": "https://json.schemastore.org/prettierrc", 71 | "singleQuote": true, 72 | "semi": false, 73 | "trailingComma": "none" 74 | } 75 | ``` 76 | 77 | ::: 78 | 79 | 3. Format your code. 80 | 81 | ::: code-group 82 | 83 | ```bash [npm] 84 | $ npm run format 85 | ``` 86 | 87 | ```bash [yarn] 88 | $ yarn run format 89 | ``` 90 | 91 | ```bash [pnpm] 92 | $ pnpm run format 93 | ``` 94 | 95 | ```bash [bun] 96 | $ bun run format 97 | ``` 98 | 99 | ::: 100 | 101 | ::: tip 102 | For more details about Prettier refer to the [Prettier docs](https://prettier.io). 103 | ::: 104 | -------------------------------------------------------------------------------- /docs/integrations/dev/storybook.md: -------------------------------------------------------------------------------- 1 | # Storybook 2 | 3 | [Storybook](https://storybook.js.org) is a frontend workshop for building UI components and pages in isolation. Made for UI development, testing, and documentation. 4 | 5 | You can use the [Minze CLI](/guide/installation#cli), with the `storybook` template to scaffold a Minze + Storybook environment. Alternatively have a look at the or the [Storybook template](https://github.com/sergejcodes/minze/tree/main/packages/create-minze/template-storybook) in the Minze repo. 6 | 7 | ::: code-group 8 | 9 | ```bash [npm] 10 | $ npm create minze@latest -- --template storybook 11 | ``` 12 | 13 | ```bash [yarn] 14 | $ yarn create minze --template storybook 15 | ``` 16 | 17 | ```bash [pnpm] 18 | $ pnpm create minze -- --template storybook 19 | ``` 20 | 21 | ```bash [bun] 22 | $ bun create minze --template storybook 23 | ``` 24 | 25 | ::: 26 | -------------------------------------------------------------------------------- /docs/integrations/dev/vite.md: -------------------------------------------------------------------------------- 1 | # Vite 2 | 3 | The default Minze dev environment runs with Vite. If you want to build a components library have a look at the [CLI Installation Guide](/guide/installation#cli) or the [Vite Template](https://github.com/sergejcodes/minze/tree/main/packages/create-minze/template-vite) in the Minze repo. 4 | 5 | If you want to add Minze to a fresh Vite project follow these steps: 6 | 7 | 1. Install dependencies. 8 | 9 | ::: code-group 10 | 11 | ```bash [npm] 12 | $ npm add minze 13 | ``` 14 | 15 | ```bash [yarn] 16 | $ yarn add minze 17 | ``` 18 | 19 | ```bash [pnpm] 20 | $ pnpm add minze 21 | ``` 22 | 23 | ```bash [bun] 24 | $ bun add minze 25 | ``` 26 | 27 | ::: 28 | 29 | 2. Set `keepNames` to true, to keep class names as is. 30 | 31 | ::: code-group 32 | 33 | ```js [vite.config.js] 34 | import { defineConfig } from 'vite' 35 | 36 | export default defineConfig({ 37 | esbuild: { 38 | keepNames: true 39 | } 40 | }) 41 | ``` 42 | 43 | ::: 44 | 45 | 3. Import `MinzeElement` and define a component. 46 | 47 | ```js 48 | import { MinzeElement } from 'minze' 49 | 50 | class MyElement extends MinzeElement { 51 | html = () => 'Hello Minze!' 52 | } 53 | 54 | MyElement.define() 55 | ``` 56 | 57 | 4. Add the component to any of your markup templates. 58 | 59 | ```html 60 | 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/integrations/dev/vitest.md: -------------------------------------------------------------------------------- 1 | # Vitest 2 | 3 | If you used the [CLI method](/guide/installation#cli) to install Minze you can extend your environment with unit tests quite quickly by using [Vitest](https://vitest.dev). 4 | 5 | > The following guide is based on a fresh Minze CLI installation. 6 | 7 | 1. Install dependencies. 8 | 9 | ::: code-group 10 | 11 | ```bash [npm] 12 | $ npm add -D vitest @vitest/ui happy-dom 13 | ``` 14 | 15 | ```bash [yarn] 16 | $ yarn add -D vitest @vitest/ui happy-dom 17 | ``` 18 | 19 | ```bash [pnpm] 20 | $ pnpm add -D vitest @vitest/ui happy-dom 21 | ``` 22 | 23 | ```bash [bun] 24 | $ bun add -D vitest @vitest/ui happy-dom 25 | ``` 26 | 27 | ::: 28 | 29 | 2. Add test scripts to `package.json`. 30 | 31 | ::: code-group 32 | 33 | ```json [package.json] 34 | { 35 | "scripts": { 36 | // ... 37 | "test": "vitest", 38 | "test-ui": "vitest --ui" 39 | } 40 | } 41 | ``` 42 | 43 | ::: 44 | 45 | 3. Set the Vitest environment to `happy-dom` inside the vite config file. 46 | 47 | ::: code-group 48 | 49 | 50 | ```js [vite.config.js] 51 | import { defineConfig } from 'vite' 52 | import minze from 'vite-plugin-minze' 53 | 54 | export default defineConfig({ 55 | resolve: { 56 | alias: { '@': new URL('./src', import.meta.url).pathname } 57 | }, 58 | test: { // [!code ++] 59 | environment: 'happy-dom' // [!code ++] 60 | }, // [!code ++] 61 | plugins: [minze()] 62 | }) 63 | ``` 64 | 65 | 66 | ::: 67 | 68 | 4. Create a `my-button.test.js` file inside the `src/lib` directory. 69 | 70 | ``` 71 | src/ 72 | └─ lib/ 73 | ├─ ... 74 | ├─ my-button.js 75 | └─ my-button.test.js // [!code ++] 76 | ``` 77 | 78 | 5. Add the following code to your newly created file: 79 | 80 | ::: code-group 81 | 82 | ```js [src/lib/my-button.test.js] 83 | import { test, expect } from 'vitest' 84 | import { MyButton } from './my-button' 85 | 86 | test('my-button', () => { 87 | expect(MyButton.name).toBe('MyButton') 88 | // ... 89 | }) 90 | ``` 91 | 92 | ::: 93 | 94 | 6. Run the test script. 95 | 96 | ::: code-group 97 | 98 | ```bash [npm] 99 | $ npm test 100 | ``` 101 | 102 | ```bash [yarn] 103 | $ yarn test 104 | ``` 105 | 106 | ```bash [pnpm] 107 | $ pnpm test 108 | ``` 109 | 110 | ```bash [bun] 111 | $ bun test 112 | ``` 113 | 114 | ::: 115 | 116 | ::: tip 117 | For more details about Vitest refer to the [Vitest docs](https://vitest.dev/). 118 | ::: 119 | -------------------------------------------------------------------------------- /docs/integrations/index.md: -------------------------------------------------------------------------------- 1 | # Integrations 2 | 3 | Since Minze is build on top of Web Components and doesn't rely on a specific framework, it can be integrated into a variety of different workflows. 4 | 5 | Have a look at the [Installation Guide](/guide/installation) if you want to integrate Minze into your workflow, or browse the available integration guides to integrate other tools into the Minze dev environment. 6 | -------------------------------------------------------------------------------- /docs/public/CNAME: -------------------------------------------------------------------------------- 1 | minze.dev -------------------------------------------------------------------------------- /docs/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergejcodes/minze/d6fa585a933d7a2477e8c49992e7bd8f4885cf5b/docs/public/favicon.png -------------------------------------------------------------------------------- /docs/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergejcodes/minze/d6fa585a933d7a2477e8c49992e7bd8f4885cf5b/docs/public/logo.png -------------------------------------------------------------------------------- /docs/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/public/social.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergejcodes/minze/d6fa585a933d7a2477e8c49992e7bd8f4885cf5b/docs/public/social.jpg -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import ts from 'typescript-eslint' 3 | import prettier from 'eslint-config-prettier' 4 | import globals from 'globals' 5 | 6 | /** @type {import('eslint').Linter.Config[]} */ 7 | export default [ 8 | js.configs.recommended, 9 | ...ts.configs.recommended, 10 | prettier, 11 | { 12 | languageOptions: { 13 | globals: { 14 | ...globals.browser, 15 | ...globals.node 16 | } 17 | } 18 | }, 19 | { 20 | rules: { 21 | '@typescript-eslint/no-unsafe-declaration-merging': 'off', 22 | '@typescript-eslint/no-explicit-any': 'off' 23 | } 24 | }, 25 | { 26 | ignores: ['**/dist/', '**/storybook/'] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minze-monorepo", 3 | "private": true, 4 | "type": "module", 5 | "engines": { 6 | "node": ">=v16.0.0" 7 | }, 8 | "workspaces": [ 9 | "packages/*" 10 | ], 11 | "scripts": { 12 | "prepare": "npm run stub", 13 | "dev": "npm run dev -w minze", 14 | "stub": "npm run stub --workspaces --if-present", 15 | "build": "npm run build --workspaces --if-present", 16 | "test": "npm run test --workspaces --if-present", 17 | "docs": "vitepress dev docs", 18 | "docs-build": "vitepress build docs", 19 | "docs-preview": "vitepress preview docs", 20 | "format": "prettier --write --cache .", 21 | "lint": "eslint --cache .", 22 | "deps": "(npx -y taze -r -w) && (npm i)", 23 | "postinstall": "simple-git-hooks" 24 | }, 25 | "devDependencies": { 26 | "@types/node": "^22.15.21", 27 | "eslint": "^9.27.0", 28 | "eslint-config-prettier": "^10.1.5", 29 | "globals": "^16.1.0", 30 | "lint-staged": "^16.0.0", 31 | "prettier": "3.5.3", 32 | "simple-git-hooks": "^2.13.0", 33 | "typescript-eslint": "^8.32.1", 34 | "vitepress": "^1.6.3" 35 | }, 36 | "simple-git-hooks": { 37 | "pre-commit": "npx lint-staged", 38 | "commit-msg": "npx @sergejcodes/verify-commit-msg $1" 39 | }, 40 | "lint-staged": { 41 | "*": [ 42 | "prettier --write --cache --ignore-unknown" 43 | ], 44 | "packages/**/*.{ts,js}": [ 45 | "eslint --cache --fix" 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/create-minze/.bumpfile: -------------------------------------------------------------------------------- 1 | 31410 2 | -------------------------------------------------------------------------------- /packages/create-minze/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-present, Sergej Samsonenko 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /packages/create-minze/README.md: -------------------------------------------------------------------------------- 1 | # Create Minze 2 | 3 | ## Scaffolding a Minze dev environment 4 | 5 | > Minze requires [Node.js](https://nodejs.dev/) version >= `16.0.0` 6 | 7 | **npm** 8 | 9 | ```bash 10 | npm create minze@latest 11 | ``` 12 | 13 | **yarn** 14 | 15 | ```bash 16 | yarn create minze 17 | ``` 18 | 19 | **pnpm** 20 | 21 | ```bash 22 | pnpm create minze 23 | ``` 24 | 25 | Then follow the prompts! 26 | 27 | ### Command Line Options 28 | 29 | You can also directly specify the project name and the template you want to use via additional command line options. For example, to scaffold a Vite environment, run: 30 | 31 | ```bash 32 | # npm 33 | npm create minze@latest my-project -- --template vite 34 | 35 | # yarn 36 | yarn create minze my-project --template vite 37 | 38 | # pnpm 39 | pnpm create minze my-project -- --template vite 40 | ``` 41 | 42 | Currently supported template presets include: 43 | 44 | - `vite` 45 | - `vite-ts` 46 | - `storybook` 47 | - `storybook-ts` 48 | 49 | You can use `.` for the project name to scaffold in the current directory. 50 | -------------------------------------------------------------------------------- /packages/create-minze/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import './dist/index.js' 4 | -------------------------------------------------------------------------------- /packages/create-minze/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-minze", 3 | "version": "2.2.2", 4 | "license": "MIT", 5 | "author": "Sergej Samsonenko", 6 | "description": "Minze scaffolding tool.", 7 | "keywords": [ 8 | "minze", 9 | "minzejs", 10 | "scaffolding", 11 | "starter", 12 | "template" 13 | ], 14 | "type": "module", 15 | "bin": { 16 | "create-minze": "index.js" 17 | }, 18 | "scripts": { 19 | "build": "tsc", 20 | "dev": "tsc --watch", 21 | "typecheck": "tsc --noEmit", 22 | "test": "vitest --run" 23 | }, 24 | "main": "index.js", 25 | "files": [ 26 | "index.js", 27 | "template-*/**/*", 28 | "dist" 29 | ], 30 | "engines": { 31 | "node": ">=16.0.0" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/sergejcodes/minze.git", 36 | "directory": "packages/create-minze" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/sergejcodes/minze/issues" 40 | }, 41 | "homepage": "https://github.com/sergejcodes/minze/tree/main/packages/create-minze#readme", 42 | "dependencies": { 43 | "kolorist": "^1.8.0", 44 | "minimist": "^1.2.8", 45 | "prompts": "^2.4.2" 46 | }, 47 | "devDependencies": { 48 | "@types/fs-extra": "^11.0.4", 49 | "@types/minimist": "^1.2.5", 50 | "@types/prompts": "^2.4.9", 51 | "execa": "^9.5.3", 52 | "fs-extra": "^11.3.0", 53 | "typescript": "^5.8.3", 54 | "vitest": "^3.1.4" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/create-minze/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'node:path' 2 | import type { SyncOptions, SyncResult } from 'execa' 3 | import { execaCommandSync } from 'execa' 4 | import fs from 'fs-extra' 5 | import { afterEach, beforeAll, test, expect } from 'vitest' 6 | 7 | const CLI_PATH = join(__dirname, '..') 8 | 9 | const projectName = 'test-app' 10 | const genPath = join(__dirname, projectName) 11 | 12 | const run = (args: string[], options: SyncOptions = {}): SyncResult => { 13 | return execaCommandSync(`node ${CLI_PATH} ${args.join(' ')}`, options) 14 | } 15 | 16 | // Helper to create a non-empty directory 17 | const createNonEmptyDir = () => { 18 | // Create the temporary directory 19 | fs.mkdirpSync(genPath) 20 | 21 | // Create a package.json file 22 | const pkgJson = join(genPath, 'package.json') 23 | fs.writeFileSync(pkgJson, '{ "foo": "bar" }') 24 | } 25 | 26 | // Vite starter template 27 | const templateFiles = fs 28 | .readdirSync(join(CLI_PATH, 'template-vite')) 29 | // _gitignore is renamed to .gitignore 30 | .map((filePath: unknown) => 31 | filePath === '_gitignore' ? '.gitignore' : filePath 32 | ) 33 | .sort() 34 | 35 | beforeAll(() => fs.remove(genPath)) 36 | afterEach(() => fs.remove(genPath)) 37 | 38 | test('prompts for the project name if none supplied', () => { 39 | const { stdout } = run([]) 40 | expect(stdout).toContain('Project name:') 41 | }) 42 | 43 | test('prompts for the framework if none supplied when target dir is current directory', () => { 44 | fs.mkdirpSync(genPath) 45 | const { stdout } = run(['.'], { cwd: genPath }) 46 | expect(stdout).toContain('Select a framework:') 47 | }) 48 | 49 | test('prompts for the framework if none supplied', () => { 50 | const { stdout } = run([projectName]) 51 | expect(stdout).toContain('Select a framework:') 52 | }) 53 | 54 | test('prompts for the framework on not supplying a value for --template', () => { 55 | const { stdout } = run([projectName, '--template']) 56 | expect(stdout).toContain('Select a framework:') 57 | }) 58 | 59 | test('prompts for the framework on supplying an invalid template', () => { 60 | const { stdout } = run([projectName, '--template', 'unknown']) 61 | expect(stdout).toContain( 62 | `"unknown" isn't a valid template. Please choose from below:` 63 | ) 64 | }) 65 | 66 | test('asks to overwrite non-empty target directory', () => { 67 | createNonEmptyDir() 68 | const { stdout } = run([projectName], { cwd: __dirname }) 69 | expect(stdout).toContain(`Target directory "${projectName}" is not empty.`) 70 | }) 71 | 72 | test('asks to overwrite non-empty current directory', () => { 73 | createNonEmptyDir() 74 | const { stdout } = run(['.'], { cwd: genPath }) 75 | expect(stdout).toContain(`Current directory is not empty.`) 76 | }) 77 | 78 | test('successfully scaffolds a project based on vite starter template', () => { 79 | const { stdout } = run([projectName, '--template', 'vite'], { 80 | cwd: __dirname 81 | }) 82 | const generatedFiles = fs.readdirSync(genPath).sort() 83 | 84 | // Assertions 85 | expect(stdout).toContain(`Scaffolding project in ${genPath}`) 86 | expect(templateFiles).toEqual(generatedFiles) 87 | }) 88 | 89 | test('works with the -t alias', () => { 90 | const { stdout } = run([projectName, '-t', 'vite'], { 91 | cwd: __dirname 92 | }) 93 | const generatedFiles = fs.readdirSync(genPath).sort() 94 | 95 | // Assertions 96 | expect(stdout).toContain(`Scaffolding project in ${genPath}`) 97 | expect(templateFiles).toEqual(generatedFiles) 98 | }) 99 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/.storybook/global.css: -------------------------------------------------------------------------------- 1 | :not(:defined) { 2 | visibility: hidden; 3 | } 4 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/web-components-vite' 2 | 3 | const config: StorybookConfig = { 4 | stories: ['../src/lib/**/*.mdx', '../src/lib/**/*.stories.@(ts|js)'], 5 | addons: [ 6 | '@storybook/addon-links', 7 | '@storybook/addon-essentials', 8 | '@chromatic-com/storybook' 9 | ], 10 | framework: { name: '@storybook/web-components-vite', options: {} } 11 | } 12 | 13 | export default config 14 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from '@storybook/web-components' 2 | import './global.css' 3 | 4 | import { modules, defineAll } from '../src/main' 5 | defineAll(modules) 6 | 7 | const preview: Preview = { 8 | parameters: { 9 | layout: 'centered' 10 | } 11 | } 12 | 13 | export default preview 14 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/_gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | *.dev.html 5 | storybook -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minze-storybook-ts", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "main": "dist/main.js", 6 | "types": "dist/main.d.ts", 7 | "files": [ 8 | "dist", 9 | "storybook" 10 | ], 11 | "scripts": { 12 | "dev": "storybook dev -p 5173", 13 | "build": "(storybook build --docs -o storybook) && (vite build)", 14 | "preview": "vite preview --outDir storybook" 15 | }, 16 | "dependencies": { 17 | "minze": "^1.10.1" 18 | }, 19 | "devDependencies": { 20 | "@chromatic-com/storybook": "^2.0.2", 21 | "@storybook/addon-essentials": "^8.6.14", 22 | "@storybook/addon-links": "^8.6.14", 23 | "@storybook/blocks": "^8.6.14", 24 | "@storybook/test": "^8.6.14", 25 | "@storybook/web-components": "^8.6.14", 26 | "@storybook/web-components-vite": "^8.6.14", 27 | "lit": "^3.3.0", 28 | "storybook": "^8.6.14", 29 | "typescript": "^5.8.3", 30 | "vite": "^6.3.5", 31 | "vite-plugin-dts": "^4.5.4", 32 | "vite-plugin-minze": "^1.1.5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/Introduction.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/blocks' 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Minze + Storybook

16 | 17 |
18 |
    19 |
  • Minze is a dead-simple JS framework for native web components.
  • 20 |
  • 21 | Storybook is a frontend workshop for building UI components and pages in 22 | isolation. Made for UI development, testing, and documentation. 23 |
  • 24 |
25 | 26 |

Browse example stories by navigating to them in the sidebar.

27 | 28 |

29 | View the code for components and stories in the src/lib 30 | directory to learn how they work. 31 |

32 |
33 | 34 | 65 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/logos/minze-logo.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/web-components' 2 | import { html } from 'lit' 3 | 4 | const meta: Meta = { 5 | title: 'Components/Logos/minze-logo', 6 | component: 'minze-logo', 7 | tags: ['autodocs'], 8 | render: ({ width, height }) => 9 | html`` 10 | } 11 | 12 | export default meta 13 | 14 | type Story = StoryObj 15 | 16 | export const Default: Story = { 17 | args: { 18 | width: 146, 19 | height: 60 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/logos/minze-logo.ts: -------------------------------------------------------------------------------- 1 | import type { Attrs } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MinzeLogo { 5 | width: string 6 | height: string 7 | } 8 | 9 | export class MinzeLogo extends MinzeElement { 10 | attrs: Attrs = [ 11 | ['width', 347], 12 | ['height', 146] 13 | ] 14 | 15 | static observedAttributes = ['width', 'height'] 16 | 17 | html = () => ` 18 | 19 | ` 20 | 21 | css = () => ` 22 | :host { 23 | display: inline-block; 24 | } 25 | ` 26 | } 27 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/logos/storybook-logo.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/web-components' 2 | import { html } from 'lit' 3 | 4 | const meta: Meta = { 5 | title: 'Components/Logos/storybook-logo', 6 | component: 'storybook-logo', 7 | tags: ['autodocs'], 8 | render: ({ width, height }) => 9 | html`` 10 | } 11 | 12 | export default meta 13 | 14 | type Story = StoryObj 15 | 16 | export const Default: Story = { 17 | args: { 18 | width: 68, 19 | height: 80 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/logos/storybook-logo.ts: -------------------------------------------------------------------------------- 1 | import type { Attrs } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface StorybookLogo { 5 | width: string 6 | height: string 7 | } 8 | 9 | export class StorybookLogo extends MinzeElement { 10 | attrs: Attrs = [ 11 | ['width', 17], 12 | ['height', 20] 13 | ] 14 | 15 | static observedAttributes = ['width', 'height'] 16 | 17 | html = () => ` 18 | 19 | 20 | 21 | 22 | 23 | ` 24 | 25 | css = () => ` 26 | :host { 27 | display: inline-block; 28 | } 29 | ` 30 | } 31 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/my-button.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/web-components' 2 | import { html } from 'lit' 3 | 4 | const meta: Meta = { 5 | title: 'Components/Buttons/my-button', 6 | component: 'my-button', 7 | tags: ['autodocs'], 8 | render: ({ text }) => html`${text}` 9 | } 10 | 11 | export default meta 12 | 13 | type Story = StoryObj 14 | 15 | export const Default: Story = { 16 | args: { 17 | text: 'Hello Minze!' 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/my-button.ts: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MyButton extends MinzeElement { 4 | html = () => ` 5 | 8 | ` 9 | 10 | css = () => ` 11 | button { 12 | display: inline-block; 13 | border-radius: 8px; 14 | border: 1px solid transparent; 15 | padding: 0.6em 1.2em; 16 | font-size: 1em; 17 | font-weight: 500; 18 | font-family: inherit; 19 | color: currentColor; 20 | background-color: rgb(249 249 249); 21 | cursor: pointer; 22 | user-select: none; 23 | position: relative; 24 | transition: border-color 0.25s; 25 | } 26 | 27 | button:hover { 28 | border-color: var(--color-primary, rgb(55 245 220)); 29 | } 30 | 31 | button:active { 32 | outline: 3px solid var(--color-primary, rgb(55 245 220)); 33 | outline-offset: -2px; 34 | } 35 | ` 36 | } 37 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/my-element.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/web-components' 2 | import { html } from 'lit' 3 | 4 | const meta: Meta = { 5 | title: 'Sections/my-element', 6 | component: 'my-element', 7 | tags: ['autodocs'], 8 | render: ({ text }) => html`

${text}

` 9 | } 10 | 11 | export default meta 12 | 13 | type Story = StoryObj 14 | 15 | export const Default: Story = { 16 | args: { 17 | text: 'Minze + Storybook' 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/lib/my-element.ts: -------------------------------------------------------------------------------- 1 | import type { Reactive } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MyElement { 5 | count: number 6 | } 7 | 8 | export class MyElement extends MinzeElement { 9 | reactive: Reactive = [['count', 0]] 10 | 11 | increaseCount = () => this.count++ 12 | 13 | text = 'Click on the Minze and Storybook logos to learn more' 14 | 15 | html = () => ` 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 |
29 | 30 | count is ${this.count} 31 | 32 |
33 | 34 |

35 | ${this.text} 36 |

37 | ` 38 | 39 | css = () => ` 40 | :host { 41 | max-width: 1280px; 42 | margin: 0 auto; 43 | padding: 2rem; 44 | text-align: center; 45 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 46 | color: rgb(33 53 71); 47 | background-color: rgb(255 255 255); 48 | } 49 | 50 | a { 51 | text-decoration: none; 52 | } 53 | 54 | .logo { 55 | padding: 1.5em; 56 | will-change: filter; 57 | transition: filter 300ms; 58 | } 59 | 60 | .logo:hover { 61 | filter: drop-shadow(0 0 2em rgb(50 255 160 / 65%)); 62 | } 63 | 64 | .logo.storybook:hover { 65 | filter: drop-shadow(0 0 2em rgb(255 70 130 / 65%)); 66 | } 67 | 68 | .card { 69 | padding: 2em; 70 | } 71 | 72 | .text { 73 | color: rgb(136 136 136); 74 | margin: 1rem 0; 75 | } 76 | 77 | ::slotted(h1) { 78 | font-size: 3.2em; 79 | line-height: 1.1; 80 | transition: rotate 750ms; 81 | ${this.count >= 5 ? 'rotate: 360deg;' : ''} 82 | } 83 | ` 84 | } 85 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/main.ts: -------------------------------------------------------------------------------- 1 | import { Minze } from 'minze' 2 | const modules = import.meta.glob('./lib/**/!(*.spec|*.test|*.stories).@(ts|js)') 3 | const defineAll = Minze.defineAll 4 | 5 | export { modules as default, modules, defineAll } 6 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/src/types/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "allowJs": false, 7 | "strict": true, 8 | "noImplicitAny": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "skipLibCheck": true, 11 | "baseUrl": ".", 12 | "paths": { 13 | "@/*": ["./src/*"] 14 | } 15 | }, 16 | "include": ["./src"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook-ts/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import minze from 'vite-plugin-minze' 3 | import dts from 'vite-plugin-dts' 4 | 5 | export default defineConfig({ 6 | resolve: { 7 | alias: { '@': new URL('./src', import.meta.url).pathname } 8 | }, 9 | plugins: [ 10 | minze(), 11 | dts({ exclude: ['src/vite.ts', 'src/**/*.{spec,test,stories}.ts'] }) 12 | ] 13 | }) 14 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/.storybook/global.css: -------------------------------------------------------------------------------- 1 | :not(:defined) { 2 | visibility: hidden; 3 | } 4 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/.storybook/main.js: -------------------------------------------------------------------------------- 1 | export default { 2 | stories: ['../src/lib/**/*.mdx', '../src/lib/**/*.stories.@(ts|js)'], 3 | addons: [ 4 | '@storybook/addon-links', 5 | '@storybook/addon-essentials', 6 | '@chromatic-com/storybook' 7 | ], 8 | framework: { name: '@storybook/web-components-vite', options: {} } 9 | } 10 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import './global.css' 2 | 3 | import { modules, defineAll } from '../src/main' 4 | defineAll(modules) 5 | 6 | export default { 7 | parameters: { 8 | layout: 'centered' 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/_gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | *.dev.html 5 | storybook -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minze-storybook", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "main": "dist/main.js", 6 | "files": [ 7 | "dist", 8 | "storybook" 9 | ], 10 | "scripts": { 11 | "dev": "storybook dev -p 5173", 12 | "build": "(storybook build --docs -o storybook) && (vite build)", 13 | "preview": "vite preview --outDir storybook" 14 | }, 15 | "dependencies": { 16 | "minze": "^1.10.1" 17 | }, 18 | "devDependencies": { 19 | "@chromatic-com/storybook": "^2.0.2", 20 | "@storybook/addon-essentials": "^8.6.14", 21 | "@storybook/addon-links": "^8.6.14", 22 | "@storybook/blocks": "^8.6.14", 23 | "@storybook/test": "^8.6.14", 24 | "@storybook/web-components": "^8.6.14", 25 | "@storybook/web-components-vite": "^8.6.14", 26 | "lit": "^3.3.0", 27 | "storybook": "^8.6.14", 28 | "vite": "^6.3.5", 29 | "vite-plugin-minze": "^1.1.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/Introduction.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/blocks' 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Minze + Storybook

16 | 17 |
18 |
    19 |
  • Minze is a dead-simple JS framework for native web components.
  • 20 |
  • 21 | Storybook is a frontend workshop for building UI components and pages in 22 | isolation. Made for UI development, testing, and documentation. 23 |
  • 24 |
25 | 26 |

Browse example stories by navigating to them in the sidebar.

27 | 28 |

29 | View the code for components and stories in the src/lib 30 | directory to learn how they work. 31 |

32 |
33 | 34 | 65 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/logos/minze-logo.js: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MinzeLogo extends MinzeElement { 4 | attrs = [ 5 | ['width', 347], 6 | ['height', 146] 7 | ] 8 | 9 | static observedAttributes = ['width', 'height'] 10 | 11 | html = () => ` 12 | 13 | ` 14 | 15 | css = () => ` 16 | :host { 17 | display: inline-block; 18 | } 19 | ` 20 | } 21 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/logos/minze-logo.stories.js: -------------------------------------------------------------------------------- 1 | import { html } from 'lit' 2 | 3 | export default { 4 | title: 'Components/Logos/minze-logo', 5 | component: 'minze-logo', 6 | tags: ['autodocs'], 7 | render: ({ width, height }) => 8 | html`` 9 | } 10 | 11 | export const Default = { 12 | args: { 13 | width: 146, 14 | height: 60 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/logos/storybook-logo.js: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class StorybookLogo extends MinzeElement { 4 | attrs = [ 5 | ['width', 17], 6 | ['height', 20] 7 | ] 8 | 9 | static observedAttributes = ['width', 'height'] 10 | 11 | html = () => ` 12 | 13 | 14 | 15 | 16 | 17 | ` 18 | 19 | css = () => ` 20 | :host { 21 | display: inline-block; 22 | } 23 | ` 24 | } 25 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/logos/storybook-logo.stories.js: -------------------------------------------------------------------------------- 1 | import { html } from 'lit' 2 | 3 | export default { 4 | title: 'Components/Logos/storybook-logo', 5 | component: 'storybook-logo', 6 | tags: ['autodocs'], 7 | render: ({ width, height }) => 8 | html`` 9 | } 10 | 11 | export const Default = { 12 | args: { 13 | width: 68, 14 | height: 80 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/my-button.js: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MyButton extends MinzeElement { 4 | html = () => ` 5 | 8 | ` 9 | 10 | css = () => ` 11 | button { 12 | display: inline-block; 13 | border-radius: 8px; 14 | border: 1px solid transparent; 15 | padding: 0.6em 1.2em; 16 | font-size: 1em; 17 | font-weight: 500; 18 | font-family: inherit; 19 | color: currentColor; 20 | background-color: rgb(249 249 249); 21 | cursor: pointer; 22 | user-select: none; 23 | position: relative; 24 | transition: border-color 0.25s; 25 | } 26 | 27 | button:hover { 28 | border-color: var(--color-primary, rgb(55 245 220)); 29 | } 30 | 31 | button:active { 32 | outline: 3px solid var(--color-primary, rgb(55 245 220)); 33 | outline-offset: -2px; 34 | } 35 | ` 36 | } 37 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/my-button.stories.js: -------------------------------------------------------------------------------- 1 | import { html } from 'lit' 2 | 3 | export default { 4 | title: 'Components/Buttons/my-button', 5 | component: 'my-button', 6 | tags: ['autodocs'], 7 | render: ({ text }) => html`${text}` 8 | } 9 | 10 | export const Default = { 11 | args: { 12 | text: 'Hello Minze!' 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/my-element.js: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MyElement extends MinzeElement { 4 | reactive = [['count', 0]] 5 | 6 | increaseCount = () => this.count++ 7 | 8 | text = 'Click on the Minze and Storybook logos to learn more' 9 | 10 | html = () => ` 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 |
24 | 25 | count is ${this.count} 26 | 27 |
28 | 29 |

30 | ${this.text} 31 |

32 | ` 33 | 34 | css = () => ` 35 | :host { 36 | max-width: 1280px; 37 | margin: 0 auto; 38 | padding: 2rem; 39 | text-align: center; 40 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 41 | color: rgb(33 53 71); 42 | background-color: rgb(255 255 255); 43 | } 44 | 45 | a { 46 | text-decoration: none; 47 | } 48 | 49 | .logo { 50 | padding: 1.5em; 51 | will-change: filter; 52 | transition: filter 300ms; 53 | } 54 | 55 | .logo:hover { 56 | filter: drop-shadow(0 0 2em rgb(50 255 160 / 65%)); 57 | } 58 | 59 | .logo.storybook:hover { 60 | filter: drop-shadow(0 0 2em rgb(255 70 130 / 65%)); 61 | } 62 | 63 | .card { 64 | padding: 2em; 65 | } 66 | 67 | .text { 68 | color: rgb(136 136 136); 69 | margin: 1rem 0; 70 | } 71 | 72 | ::slotted(h1) { 73 | font-size: 3.2em; 74 | line-height: 1.1; 75 | transition: rotate 750ms; 76 | ${this.count >= 5 ? 'rotate: 360deg;' : ''} 77 | } 78 | ` 79 | } 80 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/lib/my-element.stories.js: -------------------------------------------------------------------------------- 1 | import { html } from 'lit' 2 | 3 | export default { 4 | title: 'Sections/my-element', 5 | component: 'my-element', 6 | tags: ['autodocs'], 7 | render: ({ text }) => html`

${text}

` 8 | } 9 | 10 | export const Default = { 11 | args: { 12 | text: 'Minze + Storybook' 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/src/main.js: -------------------------------------------------------------------------------- 1 | import { Minze } from 'minze' 2 | const modules = import.meta.glob('./lib/**/!(*.spec|*.test|*.stories).js') 3 | const defineAll = Minze.defineAll 4 | 5 | export { modules as default, modules, defineAll } 6 | -------------------------------------------------------------------------------- /packages/create-minze/template-storybook/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import minze from 'vite-plugin-minze' 3 | 4 | export default defineConfig({ 5 | resolve: { 6 | alias: { '@': new URL('./src', import.meta.url).pathname } 7 | }, 8 | plugins: [minze()] 9 | }) 10 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/_gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | *.dev.html -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Minze + Vite Starter 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minze-vite-ts", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "main": "dist/main.js", 6 | "types": "dist/main.d.ts", 7 | "files": [ 8 | "dist" 9 | ], 10 | "scripts": { 11 | "dev": "vite", 12 | "build": "vite build", 13 | "preview": "vite preview" 14 | }, 15 | "dependencies": { 16 | "minze": "^1.10.1" 17 | }, 18 | "devDependencies": { 19 | "typescript": "^5.8.3", 20 | "vite": "^6.3.5", 21 | "vite-plugin-dts": "^4.5.4", 22 | "vite-plugin-minze": "^1.1.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/public/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Minze + Vite Starter (Preview) 8 | 13 | 14 | 15 | 16 |

Minze + Vite

17 |
18 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/assets/vite.css: -------------------------------------------------------------------------------- 1 | :root { 2 | color-scheme: light dark; 3 | 4 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 5 | line-height: 1.5; 6 | font-weight: 400; 7 | 8 | font-synthesis: none; 9 | text-rendering: optimizeLegibility; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | -webkit-text-size-adjust: 100%; 13 | } 14 | 15 | * { 16 | box-sizing: border-box; 17 | } 18 | 19 | html, 20 | body { 21 | margin: 0; 22 | } 23 | 24 | #app { 25 | display: flex; 26 | place-items: center; 27 | min-width: 320px; 28 | min-height: 100vh; 29 | } 30 | 31 | :not(:defined) { 32 | visibility: hidden; 33 | } 34 | 35 | @media (prefers-color-scheme: light) { 36 | :root { 37 | color: rgb(33 53 71); 38 | background-color: rgb(255 255 255); 39 | } 40 | } 41 | 42 | @media (prefers-color-scheme: dark) { 43 | :root { 44 | color: rgb(255 255 255 / 87%); 45 | background-color: rgb(36 36 36); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/lib/logos/minze-logo.ts: -------------------------------------------------------------------------------- 1 | import type { Attrs } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MinzeLogo { 5 | width: string 6 | height: string 7 | } 8 | 9 | export class MinzeLogo extends MinzeElement { 10 | attrs: Attrs = [ 11 | ['width', 347], 12 | ['height', 146] 13 | ] 14 | 15 | static observedAttributes = ['width', 'height'] 16 | 17 | html = () => ` 18 | 19 | ` 20 | 21 | css = () => ` 22 | :host { 23 | display: inline-block; 24 | } 25 | ` 26 | } 27 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/lib/logos/vite-logo.ts: -------------------------------------------------------------------------------- 1 | import type { Attrs } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface ViteLogo { 5 | width: string 6 | height: string 7 | } 8 | 9 | export class ViteLogo extends MinzeElement { 10 | attrs: Attrs = [ 11 | ['width', 256], 12 | ['height', 257] 13 | ] 14 | 15 | static observedAttributes = ['width', 'height'] 16 | 17 | html = () => ` 18 | 19 | ` 20 | 21 | css = () => ` 22 | :host { 23 | display: inline-block; 24 | } 25 | ` 26 | } 27 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/lib/my-button.ts: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MyButton extends MinzeElement { 4 | html = () => ` 5 | 8 | ` 9 | 10 | css = () => ` 11 | button { 12 | display: inline-block; 13 | border-radius: 8px; 14 | border: 1px solid transparent; 15 | padding: 0.6em 1.2em; 16 | font-size: 1em; 17 | font-weight: 500; 18 | font-family: inherit; 19 | color: currentColor; 20 | background-color: rgb(249 249 249); 21 | cursor: pointer; 22 | user-select: none; 23 | position: relative; 24 | transition: border-color 0.25s; 25 | } 26 | 27 | button:hover { 28 | border-color: var(--color-primary, rgb(55 245 220)); 29 | } 30 | 31 | button:active { 32 | outline: 3px solid var(--color-primary, rgb(55 245 220)); 33 | outline-offset: -2px; 34 | } 35 | 36 | @media (prefers-color-scheme: dark) { 37 | button { 38 | color: currentColor; 39 | background-color: rgb(26 26 26); 40 | } 41 | } 42 | ` 43 | } 44 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/lib/my-element.ts: -------------------------------------------------------------------------------- 1 | import type { Reactive } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MyElement { 5 | count: number 6 | } 7 | 8 | export class MyElement extends MinzeElement { 9 | reactive: Reactive = [['count', 0]] 10 | 11 | increaseCount = () => this.count++ 12 | 13 | text = 'Click on the Minze and Vite logos to learn more' 14 | 15 | html = () => ` 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 |
29 | 30 | count is ${this.count} 31 | 32 |
33 | 34 |

35 | ${this.text} 36 |

37 | ` 38 | 39 | css = () => ` 40 | :host { 41 | max-width: 1280px; 42 | margin: 0 auto; 43 | padding: 2rem; 44 | text-align: center; 45 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 46 | color: rgb(33 53 71); 47 | background-color: rgb(255 255 255); 48 | } 49 | 50 | @media (prefers-color-scheme: dark) { 51 | :host { 52 | color: rgb(255 255 255 / 87%); 53 | background-color: rgb(36 36 36); 54 | } 55 | } 56 | 57 | a { 58 | text-decoration: none; 59 | } 60 | 61 | .logo { 62 | padding: 1.5em; 63 | will-change: filter; 64 | transition: filter 300ms; 65 | } 66 | 67 | .logo:hover { 68 | filter: drop-shadow(0 0 2em rgb(50 255 160 / 65%)); 69 | } 70 | 71 | .logo.vite:hover { 72 | filter: drop-shadow(0 0 2em rgb(100 108 255 / 65%)); 73 | } 74 | 75 | .card { 76 | padding: 2em; 77 | } 78 | 79 | .text { 80 | color: rgb(136 136 136); 81 | margin: 1rem 0; 82 | } 83 | 84 | ::slotted(h1) { 85 | font-size: 3.2em; 86 | line-height: 1.1; 87 | transition: rotate 750ms; 88 | ${this.count >= 5 ? 'rotate: 360deg;' : ''} 89 | } 90 | ` 91 | } 92 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/main.ts: -------------------------------------------------------------------------------- 1 | import { Minze } from 'minze' 2 | const modules = import.meta.glob('./lib/**/!(*.spec|*.test|*.stories).@(ts|js)') 3 | const defineAll = Minze.defineAll 4 | 5 | export { modules as default, modules, defineAll } 6 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/preview.html: -------------------------------------------------------------------------------- 1 | 2 |

Minze + Vite

3 |
4 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/types/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/src/vite.ts: -------------------------------------------------------------------------------- 1 | import './assets/vite.css' 2 | 3 | import { modules, defineAll } from './main' 4 | defineAll(modules) 5 | 6 | const previews = import.meta.glob('./*.html', { 7 | eager: true, 8 | query: '?raw', 9 | import: 'default' 10 | }) 11 | const preview = previews['./preview.dev.html'] ?? previews['./preview.html'] 12 | 13 | const app = document.querySelector('#app') 14 | if (app) app.innerHTML = preview 15 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "allowJs": false, 7 | "strict": true, 8 | "noImplicitAny": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "skipLibCheck": true, 11 | "baseUrl": ".", 12 | "paths": { 13 | "@/*": ["./src/*"] 14 | } 15 | }, 16 | "include": ["./src"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite-ts/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import minze from 'vite-plugin-minze' 3 | import dts from 'vite-plugin-dts' 4 | 5 | export default defineConfig({ 6 | resolve: { 7 | alias: { '@': new URL('./src', import.meta.url).pathname } 8 | }, 9 | plugins: [ 10 | minze(), 11 | dts({ exclude: ['src/vite.ts', 'src/**/*.{spec,test,stories}.ts'] }) 12 | ] 13 | }) 14 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/_gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | *.dev.html -------------------------------------------------------------------------------- /packages/create-minze/template-vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Minze + Vite Starter 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minze-vite", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "main": "dist/main.js", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "dev": "vite", 11 | "build": "vite build", 12 | "preview": "vite preview" 13 | }, 14 | "dependencies": { 15 | "minze": "^1.10.1" 16 | }, 17 | "devDependencies": { 18 | "vite": "^6.3.5", 19 | "vite-plugin-minze": "^1.1.5" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/public/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Minze + Vite Starter (Preview) 8 | 13 | 14 | 15 | 16 |

Minze + Vite

17 |
18 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/src/assets/vite.css: -------------------------------------------------------------------------------- 1 | :root { 2 | color-scheme: light dark; 3 | 4 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 5 | line-height: 1.5; 6 | font-weight: 400; 7 | 8 | font-synthesis: none; 9 | text-rendering: optimizeLegibility; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | -webkit-text-size-adjust: 100%; 13 | } 14 | 15 | * { 16 | box-sizing: border-box; 17 | } 18 | 19 | html, 20 | body { 21 | margin: 0; 22 | } 23 | 24 | #app { 25 | display: flex; 26 | place-items: center; 27 | min-width: 320px; 28 | min-height: 100vh; 29 | } 30 | 31 | :not(:defined) { 32 | visibility: hidden; 33 | } 34 | 35 | @media (prefers-color-scheme: light) { 36 | :root { 37 | color: rgb(33 53 71); 38 | background-color: rgb(255 255 255); 39 | } 40 | } 41 | 42 | @media (prefers-color-scheme: dark) { 43 | :root { 44 | color: rgb(255 255 255 / 87%); 45 | background-color: rgb(36 36 36); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/src/lib/logos/minze-logo.js: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MinzeLogo extends MinzeElement { 4 | attrs = [ 5 | ['width', 347], 6 | ['height', 146] 7 | ] 8 | 9 | static observedAttributes = ['width', 'height'] 10 | 11 | html = () => ` 12 | 13 | ` 14 | 15 | css = () => ` 16 | :host { 17 | display: inline-block; 18 | } 19 | ` 20 | } 21 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/src/lib/logos/vite-logo.js: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class ViteLogo extends MinzeElement { 4 | attrs = [ 5 | ['width', 256], 6 | ['height', 257] 7 | ] 8 | 9 | static observedAttributes = ['width', 'height'] 10 | 11 | html = () => ` 12 | 13 | ` 14 | 15 | css = () => ` 16 | :host { 17 | display: inline-block; 18 | } 19 | ` 20 | } 21 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/src/lib/my-button.js: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MyButton extends MinzeElement { 4 | html = () => ` 5 | 8 | ` 9 | 10 | css = () => ` 11 | button { 12 | display: inline-block; 13 | border-radius: 8px; 14 | border: 1px solid transparent; 15 | padding: 0.6em 1.2em; 16 | font-size: 1em; 17 | font-weight: 500; 18 | font-family: inherit; 19 | color: currentColor; 20 | background-color: rgb(249 249 249); 21 | cursor: pointer; 22 | user-select: none; 23 | position: relative; 24 | transition: border-color 0.25s; 25 | } 26 | 27 | button:hover { 28 | border-color: var(--color-primary, rgb(55 245 220)); 29 | } 30 | 31 | button:active { 32 | outline: 3px solid var(--color-primary, rgb(55 245 220)); 33 | outline-offset: -2px; 34 | } 35 | 36 | @media (prefers-color-scheme: dark) { 37 | button { 38 | color: currentColor; 39 | background-color: rgb(26 26 26); 40 | } 41 | } 42 | ` 43 | } 44 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/src/lib/my-element.js: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MyElement extends MinzeElement { 4 | reactive = [['count', 0]] 5 | 6 | increaseCount = () => this.count++ 7 | 8 | text = 'Click on the Minze and Vite logos to learn more' 9 | 10 | html = () => ` 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 |
24 | 25 | count is ${this.count} 26 | 27 |
28 | 29 |

30 | ${this.text} 31 |

32 | ` 33 | 34 | css = () => ` 35 | :host { 36 | max-width: 1280px; 37 | margin: 0 auto; 38 | padding: 2rem; 39 | text-align: center; 40 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 41 | color: rgb(33 53 71); 42 | background-color: rgb(255 255 255); 43 | } 44 | 45 | @media (prefers-color-scheme: dark) { 46 | :host { 47 | color: rgb(255 255 255 / 87%); 48 | background-color: rgb(36 36 36); 49 | } 50 | } 51 | 52 | a { 53 | text-decoration: none; 54 | } 55 | 56 | .logo { 57 | padding: 1.5em; 58 | will-change: filter; 59 | transition: filter 300ms; 60 | } 61 | 62 | .logo:hover { 63 | filter: drop-shadow(0 0 2em rgb(50 255 160 / 65%)); 64 | } 65 | 66 | .logo.vite:hover { 67 | filter: drop-shadow(0 0 2em rgb(100 108 255 / 65%)); 68 | } 69 | 70 | .card { 71 | padding: 2em; 72 | } 73 | 74 | .text { 75 | color: rgb(136 136 136); 76 | margin: 1rem 0; 77 | } 78 | 79 | ::slotted(h1) { 80 | font-size: 3.2em; 81 | line-height: 1.1; 82 | transition: rotate 750ms; 83 | ${this.count >= 5 ? 'rotate: 360deg;' : ''} 84 | } 85 | ` 86 | } 87 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/src/main.js: -------------------------------------------------------------------------------- 1 | import { Minze } from 'minze' 2 | const modules = import.meta.glob('./lib/**/!(*.spec|*.test|*.stories).js') 3 | const defineAll = Minze.defineAll 4 | 5 | export { modules as default, modules, defineAll } 6 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/src/preview.html: -------------------------------------------------------------------------------- 1 | 2 |

Minze + Vite

3 |
4 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/src/vite.js: -------------------------------------------------------------------------------- 1 | import './assets/vite.css' 2 | 3 | import { modules, defineAll } from './main' 4 | defineAll(modules) 5 | 6 | const previews = import.meta.glob('./*.html', { 7 | eager: true, 8 | query: '?raw', 9 | import: 'default' 10 | }) 11 | const preview = previews['./preview.dev.html'] ?? previews['./preview.html'] 12 | 13 | const app = document.querySelector('#app') 14 | if (app) app.innerHTML = preview 15 | -------------------------------------------------------------------------------- /packages/create-minze/template-vite/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import minze from 'vite-plugin-minze' 3 | 4 | export default defineConfig({ 5 | resolve: { 6 | alias: { '@': new URL('./src', import.meta.url).pathname } 7 | }, 8 | plugins: [minze()] 9 | }) 10 | -------------------------------------------------------------------------------- /packages/create-minze/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "moduleResolution": "bundler", 6 | "strict": true, 7 | "skipLibCheck": true, 8 | "declaration": false, 9 | "sourceMap": false, 10 | "noUnusedLocals": true, 11 | "esModuleInterop": true, 12 | "outDir": "dist" 13 | }, 14 | "include": ["./src"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/minze-vsc/.bumpfile: -------------------------------------------------------------------------------- 1 | 95 2 | -------------------------------------------------------------------------------- /packages/minze-vsc/.vscodeignore: -------------------------------------------------------------------------------- 1 | ../../** 2 | ../** 3 | node_modules 4 | test 5 | -------------------------------------------------------------------------------- /packages/minze-vsc/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023-present, Sergej Samsonenko 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /packages/minze-vsc/README.md: -------------------------------------------------------------------------------- 1 | # Minze support for Visual Studio Code 2 | 3 | > 🍃 Not sure what Minze is? Look it up at [minze.dev](https://minze.dev)! 4 | 5 | Language support for Minze. 6 | 7 | ## Features 8 | 9 | - HTML/CSS Syntax Highlighting 10 | - Code Snippets 11 | 12 | ### Syntax Highlighting 13 | 14 | The Minze VS Code extension comes bundled with snippets for auto-completion. It also adds auto-highlighting to class methods defined as arrow functions starting with `html` and `css` keyword. Additionally you can manually prefix any template literals with `/*html*/` to highlight HTML code inside them, or `/*css*/` to highlight CSS code. 15 | 16 | **Example** 17 | 18 | ```js 19 | import { MinzeElement } from Minze 20 | 21 | class MyElement extends MinzeElement { 22 | // auto 23 | html = () => `
` 24 | css = () => `:host {}` 25 | 26 | // auto 27 | htmlTemplate = () => `
` 28 | cssTemplate = () => `:host {}` 29 | 30 | // manual 31 | htmlCode = /*html*/ `
` 32 | cssCode = /*css*/ `:host {}` 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /packages/minze-vsc/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergejcodes/minze/d6fa585a933d7a2477e8c49992e7bd8f4885cf5b/packages/minze-vsc/images/logo.png -------------------------------------------------------------------------------- /packages/minze-vsc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minze-vsc", 3 | "displayName": "Minze (VSC)", 4 | "version": "0.6.5", 5 | "license": "MIT", 6 | "publisher": "sergejcodes", 7 | "author": "Sergej Samsonenko", 8 | "description": "Language support for Minze.", 9 | "keywords": [ 10 | "minze", 11 | "minzejs" 12 | ], 13 | "homepage": "https://minze.dev", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/sergejcodes/minze.git", 17 | "directory": "packages/vscode-minze" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/sergejcodes/minze/issues" 21 | }, 22 | "icon": "images/logo.png", 23 | "galleryBanner": { 24 | "color": "#1E1E20", 25 | "theme": "dark" 26 | }, 27 | "engines": { 28 | "vscode": "^1.5.0" 29 | }, 30 | "categories": [ 31 | "Programming Languages", 32 | "Snippets" 33 | ], 34 | "contributes": { 35 | "grammars": [ 36 | { 37 | "embeddedLanguages": { 38 | "meta.embedded.block.css": "css", 39 | "meta.embedded.block.html": "html" 40 | }, 41 | "injectTo": [ 42 | "source.js", 43 | "source.ts" 44 | ], 45 | "path": "./syntaxes/minze.tmLanguage.json", 46 | "scopeName": "source.js.minze.injection" 47 | } 48 | ], 49 | "snippets": [ 50 | { 51 | "path": "./snippets/common.code-snippets" 52 | }, 53 | { 54 | "language": "javascript", 55 | "path": "./snippets/javascript.json" 56 | }, 57 | { 58 | "language": "typescript", 59 | "path": "./snippets/typescript.json" 60 | } 61 | ] 62 | }, 63 | "scripts": { 64 | "vsce": "vsce", 65 | "pack": "vsce package --no-dependencies", 66 | "publish": "vsce publish --no-dependencies --no-git-tag-version --no-update-package-json" 67 | }, 68 | "devDependencies": { 69 | "@vscode/vsce": "^3.4.2" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/minze-vsc/snippets/javascript.json: -------------------------------------------------------------------------------- 1 | { 2 | "el": { 3 | "isFileTemplate": true, 4 | "prefix": "minze:el", 5 | "body": [ 6 | "import { MinzeElement } from 'minze'", 7 | "\nexport class ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}Element} extends MinzeElement {", 8 | "\t$0", 9 | "}" 10 | ], 11 | "description": "MinzeElement component base" 12 | }, 13 | 14 | "reactive": { 15 | "prefix": "_minze:reactive", 16 | "body": ["reactive = [['${1:name}', ${2:'value'}]$0]"], 17 | "description": "reactive properties" 18 | }, 19 | 20 | "attrs": { 21 | "prefix": "_minze:attrs", 22 | "body": ["attrs = [['${1:name}', ${2:'value'}]$0]"], 23 | "description": "reactive attributes" 24 | }, 25 | 26 | "watch": { 27 | "prefix": "_minze:watch", 28 | "body": [ 29 | "watch = [['${1:name}', (${2:newValue, oldValue, key, target}) => {$3}]$0]" 30 | ], 31 | "description": "reactive watch" 32 | }, 33 | 34 | "eventListeners": { 35 | "prefix": "_minze:eventListeners", 36 | "body": [ 37 | "eventListeners = [[${1|'.class','#id','[attribute=\"value\"]',this,window|}, '${2:click}', (${3:event}) => {$4}]$0]" 38 | ], 39 | "description": "event listeners" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/minze-vsc/snippets/typescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "el": { 3 | "isFileTemplate": true, 4 | "prefix": "minze:el", 5 | "body": [ 6 | "import type { Reactive, Attrs, Watch, EventListeners } from 'minze'", 7 | "import { MinzeElement } from 'minze'", 8 | "\nexport interface ${2:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}Element} {}", 9 | "\nexport class ${2:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}Element} extends MinzeElement {", 10 | "\t$0", 11 | "}" 12 | ], 13 | "description": "MinzeElement component base" 14 | }, 15 | 16 | "types": { 17 | "prefix": "minze:types", 18 | "body": [ 19 | "import type { ${1:Reactive,} ${2:Attrs,} ${3:Watch,} ${4:EventListeners} } from 'minze'" 20 | ], 21 | "description": "MinzeElement types" 22 | }, 23 | 24 | "reactive": { 25 | "prefix": "_minze:reactive", 26 | "body": ["reactive: Reactive = [['${1:name}', ${2:'value'}]$0]"], 27 | "description": "reactive properties" 28 | }, 29 | 30 | "attrs": { 31 | "prefix": "_minze:attrs", 32 | "body": ["attrs: Attrs = [['${1:name}', ${2:'value'}]$0]"], 33 | "description": "reactive attributes" 34 | }, 35 | 36 | "watch": { 37 | "prefix": "_minze:watch", 38 | "body": [ 39 | "watch: Watch = [['${1:name}', (${2:newValue, oldValue, key, target}) => {$3}]$0]" 40 | ], 41 | "description": "reactive watch" 42 | }, 43 | 44 | "eventListeners": { 45 | "prefix": "_minze:eventListeners", 46 | "body": [ 47 | "eventListeners: EventListeners = [[${1|'.class','#id','[attribute=\"value\"]',this,window|}, '${2:click}', (${3:event: Event}) => {$4}]$0]" 48 | ], 49 | "description": "event listeners" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/minze-vsc/test/minze-element.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // @ts-nocheck 3 | 4 | class MyElement extends MinzeElement { 5 | text = 'Hello Minze!' 6 | value = 'rgb(200 200 200 / 50%)' 7 | literal = `
${this.text}
` 8 | 9 | html = () => ` 10 |
11 |

${this.text}

12 |
13 | ` 14 | 15 | htmlTemplate = () => ` 16 |
17 |

${this.text}

18 |
19 | ` 20 | 21 | html1 = /* html */ `
${this.text}
` 22 | html2 = /*html*/ `
${this.text}
` 23 | html3 = html`
${this.text}
` 24 | 25 | css = () => ` 26 | :host { 27 | display: inline-block; 28 | background: ${this.value}; 29 | padding: 1rem; 30 | } 31 | ` 32 | 33 | cssTemplate = () => ` 34 | :host { 35 | display: inline-block; 36 | background: ${this.value}; 37 | padding: 1rem; 38 | } 39 | ` 40 | 41 | css1 = /* css */ `:host { background: ${this.value}; }` 42 | css2 = /*css*/ `:host { background: ${this.value}; }` 43 | css3 = css` 44 | :host { 45 | background: ${this.value}; 46 | } 47 | ` 48 | } 49 | -------------------------------------------------------------------------------- /packages/minze/.bumpfile: -------------------------------------------------------------------------------- 1 | 27949 2 | -------------------------------------------------------------------------------- /packages/minze/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-present, Sergej Samsonenko 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /packages/minze/README.md: -------------------------------------------------------------------------------- 1 | # Minze 2 | 3 | > Dead-simple JS framework for native web components. 4 | 5 | Minze (German shorthand for "Peppermint", pronounced [/ˈmɪnt͡sə/](https://upload.wikimedia.org/wikipedia/commons/c/c2/De-Minze.ogg)) lets you rapidly build native web components. 6 | 7 | It provides an intuitive abstraction layer around the web components API with its own fully typed [JavaScript API](https://minze.dev/api/). Including reactivity, lifecycle hooks, scoped styles, one-shot components registration, and more. 8 | 9 | 1. You can create cross-framework component libraries or design systems and share them with your team or the world. 10 | 2. You can add Minze to any web project and create components without even using any build tools. 11 | 12 | [Read the Docs to Learn More](https://minze.dev). 13 | 14 | ## Features 15 | 16 | - 👶 Simple - Dive right in by scaffolding a project, installing from npm or using a CDN link. 17 | - ⚡ Fast - Tiny footprint ~3KB (minified and compressed). 18 | - 🚀 Modern - Based on the latest technologies around web components. 19 | - 📦 Shareable - Build component libraries or design systems. Define once, use everywhere. 20 | - 🎲 Framework Agnostic - Use Minze with any common framework - React, Vue, Svelte, etc ... 21 | - 📕 Storybook - Minze x Storybook dev environment integration. 22 | - 📖 Extensive Docs - Comprehensive documentation and API reference. 23 | - 🔒 Typed API - Scale your component library with ease by using TypeScript. 24 | -------------------------------------------------------------------------------- /packages/minze/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Minze 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/minze/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minze", 3 | "version": "1.10.2", 4 | "license": "MIT", 5 | "author": "Sergej Samsonenko", 6 | "description": "Dead-simple JS framework for native web components.", 7 | "keywords": [ 8 | "minze", 9 | "minzejs", 10 | "web components", 11 | "web elements" 12 | ], 13 | "type": "module", 14 | "main": "dist/main.js", 15 | "types": "dist/main.d.ts", 16 | "files": [ 17 | "dist" 18 | ], 19 | "engines": { 20 | "node": ">=v16.0.0" 21 | }, 22 | "homepage": "https://minze.dev", 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/sergejcodes/minze.git", 26 | "directory": "packages/minze" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/sergejcodes/minze/issues" 30 | }, 31 | "scripts": { 32 | "dev": "vite", 33 | "build": "vite build -c vite.config.build.ts", 34 | "build-test": "vite build", 35 | "preview": "vite preview", 36 | "test": "(npm run test-vi) && (npm run test-pw)", 37 | "test-vi": "vitest --run", 38 | "test-vi-ui": "vitest --ui", 39 | "test-pw": "npx playwright test", 40 | "test-pw-ui": "npx playwright test --ui", 41 | "test-pw-debug": "npx playwright test --debug" 42 | }, 43 | "devDependencies": { 44 | "@playwright/test": "^1.52.0", 45 | "@vitest/ui": "^3.1.4", 46 | "happy-dom": "^17.4.7", 47 | "typescript": "^5.8.3", 48 | "vite": "^6.3.5", 49 | "vite-plugin-dts": "^4.5.4", 50 | "vite-plugin-minze": "*", 51 | "vitest": "^3.1.4" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/minze/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from '@playwright/test' 2 | 3 | const config: PlaywrightTestConfig = { 4 | webServer: { 5 | command: 'vite -m testing', 6 | port: 5173, 7 | reuseExistingServer: !process.env.CI 8 | }, 9 | testDir: './test' 10 | } 11 | 12 | export default config 13 | -------------------------------------------------------------------------------- /packages/minze/public/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/minze/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Minze (Preview) 8 | 13 | 14 | 15 | 16 |

Minze + Vite

17 |
18 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/minze/src/env.d.ts: -------------------------------------------------------------------------------- 1 | export declare global { 2 | const __VERSION__: string 3 | 4 | interface Document { 5 | // Experimental View Transitions API 6 | startViewTransition?: (callback: () => Promise | any) => void 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/minze/src/lib/test/minze-element.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, test, expect } from 'vitest' 2 | import { MinzeElement } from 'minze' 3 | 4 | describe('MinzeElement', () => { 5 | class TestElement extends MinzeElement {} 6 | 7 | test('version', () => { 8 | expect(MinzeElement.version).toMatch( 9 | /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/ 10 | ) 11 | }) 12 | 13 | test('isMinzeElement', () => { 14 | expect(MinzeElement.isMinzeElement).toEqual(true) 15 | }) 16 | 17 | test('name', () => { 18 | expect(TestElement.name).toBe('TestElement') 19 | }) 20 | 21 | test('dashName', () => { 22 | expect(TestElement.dashName).toBe('test-element') 23 | }) 24 | 25 | test('define', () => { 26 | TestElement.define() 27 | expect(customElements.get('test-element')).toBeTruthy() 28 | }) 29 | 30 | test('define', () => { 31 | ;(class TestElement2 extends MinzeElement {}).define('test-el') 32 | expect(customElements.get('test-el')).toBeTruthy() 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/minze/src/lib/test/minze.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, test, expect, vi } from 'vitest' 2 | import { Minze, MinzeElement } from 'minze' 3 | 4 | describe('Minze', () => { 5 | test('version', () => { 6 | expect(Minze.version).toMatch( 7 | /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/ 8 | ) 9 | }) 10 | 11 | test('define', () => { 12 | class TestElement extends MinzeElement {} 13 | Minze.define('test-el', TestElement) 14 | 15 | expect(customElements.get('test-el')).toBeTruthy() 16 | }) 17 | 18 | test('defineAll', () => { 19 | class TestElementOne extends MinzeElement {} 20 | class TestElementTwo extends MinzeElement {} 21 | 22 | Minze.defineAll([TestElementOne, TestElementTwo]) 23 | 24 | expect(customElements.get('test-element-one')).toBeTruthy() 25 | expect(customElements.get('test-element-two')).toBeTruthy() 26 | }) 27 | 28 | test('dispatch,listen,stopListen', () => { 29 | const callback = { 30 | fn: () => Minze.stopListen('minze:event', callback.fn) 31 | } 32 | 33 | const spy = vi.spyOn(callback, 'fn') 34 | 35 | Minze.listen('minze:event', callback.fn) 36 | Minze.dispatch('minze:event') 37 | Minze.dispatch('minze:event') 38 | 39 | expect(spy).toHaveBeenCalledOnce() 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /packages/minze/src/lib/test/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, test, expect, vi } from 'vitest' 2 | import { 3 | isProxy, 4 | camelToDash, 5 | dashToCamel, 6 | createObserver, 7 | warn 8 | } from '../utils' 9 | 10 | describe('isProxy', () => { 11 | test('is true', () => { 12 | const obj: Record = new Proxy( 13 | {}, 14 | { 15 | get: (target: object, prop: symbol) => { 16 | if (prop === isProxy) return true 17 | } 18 | } 19 | ) 20 | 21 | expect(obj[isProxy]).toBe(true) 22 | }) 23 | 24 | test('is false', () => { 25 | const obj: Record = {} 26 | 27 | expect(obj[isProxy]).toBeFalsy() 28 | }) 29 | }) 30 | 31 | describe('camelToDash', () => { 32 | const data = [ 33 | { input: 'camel', output: 'camel' }, 34 | { input: 'camelCase', output: 'camel-case' }, 35 | { input: 'camelCaseString', output: 'camel-case-string' }, 36 | { input: 'Pascal', output: 'pascal' }, 37 | { input: 'PascalCase', output: 'pascal-case' }, 38 | { input: 'PascalCaseString', output: 'pascal-case-string' } 39 | ] 40 | 41 | test.each(data)('transforms $input to $output', ({ input, output }) => { 42 | expect(camelToDash(input)).toBe(output) 43 | }) 44 | }) 45 | 46 | describe('dashToCamel', () => { 47 | const data = [ 48 | { input: 'dash', output: 'dash' }, 49 | { input: 'dash-case', output: 'dashCase' }, 50 | { input: 'dash-case-string', output: 'dashCaseString' } 51 | ] 52 | 53 | test.each(data)('transforms $input to $output', ({ input, output }) => { 54 | expect(dashToCamel(input)).toBe(output) 55 | }) 56 | }) 57 | 58 | describe('createObserver', () => { 59 | test('returns a Mutation Observer', () => { 60 | const element = document.createElement('div') 61 | const observer = createObserver(element, () => {}) 62 | 63 | expect(observer).toBeInstanceOf(MutationObserver) 64 | }) 65 | }) 66 | 67 | describe('warn', () => { 68 | vi.spyOn(global.console, 'warn') 69 | 70 | const data = [{ input: 'Hello Minze!', output: '[Minze warn] Hello Minze!' }] 71 | 72 | test.each(data)('takes $input and displays $output', ({ input, output }) => { 73 | warn(input) 74 | expect(console.warn).toHaveBeenCalledOnce() 75 | expect(console.warn).toBeCalledWith(output) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /packages/minze/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a symbol that makes it possible to check 3 | * if an object is a proxy. 4 | */ 5 | export const isProxy = Symbol('isProxy') 6 | 7 | /** 8 | * Converts a camelCase or PascalCase string to dash-case. 9 | * 10 | * @param value - The string to convert. 11 | * 12 | * @example 13 | * ``` 14 | * camelToDashCase('someString') // 'some-string' 15 | * ``` 16 | */ 17 | export function camelToDash(value: string) { 18 | return value 19 | .replace(/\B([A-Z])(?=[a-z])/g, '-$1') 20 | .replace(/\B([a-z0-9])([A-Z])/g, '$1-$2') 21 | .toLowerCase() 22 | } 23 | 24 | /** 25 | * Converts a dash-case string to camelCase. 26 | * 27 | * @param value - The string to convert. 28 | * 29 | * @example 30 | * ``` 31 | * dashToCamelCase('some-string') // 'someString' 32 | * ``` 33 | */ 34 | export function dashToCamel(value: string) { 35 | return value.replace(/-([a-z])/g, (g) => g[1].toUpperCase()) 36 | } 37 | 38 | /** 39 | * Creates an mutation observer and starts observation. 40 | * 41 | * @param element - The element node to observe. 42 | * @param callback - Mutation observer Callback function. 43 | * @param options - Mutation observer options. If non provided default options will be used. 44 | * 45 | * @default 46 | * options = { attributes: true, childList: true, subtree: true } 47 | * 48 | * @example 49 | * ``` 50 | * createObserver(this, () => {}, { attributes}) 51 | * ``` 52 | */ 53 | export function createObserver( 54 | element: Node | ShadowRoot, 55 | callback: MutationCallback, 56 | options: MutationObserverInit = { 57 | attributes: true, 58 | childList: true, 59 | subtree: true 60 | } 61 | ) { 62 | const observer = new MutationObserver(callback) 63 | observer.observe(element, options) 64 | 65 | return observer 66 | } 67 | 68 | /** 69 | * Logs a waring to the console. 70 | * 71 | * @param msg - The message to log. 72 | * 73 | * @example 74 | * ``` 75 | * warn('This is a warning') 76 | * ``` 77 | */ 78 | export function warn(msg: string, ...args: unknown[]) { 79 | console.warn(`[Minze warn] ${msg}`, ...args) 80 | } 81 | -------------------------------------------------------------------------------- /packages/minze/src/main.ts: -------------------------------------------------------------------------------- 1 | export { Minze as default } from './lib/minze' 2 | export * from './lib/minze' 3 | export * from './lib/minze-element' 4 | -------------------------------------------------------------------------------- /packages/minze/test/assets/vite.css: -------------------------------------------------------------------------------- 1 | :root { 2 | color-scheme: light dark; 3 | 4 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 5 | line-height: 1.5; 6 | font-weight: 400; 7 | 8 | font-synthesis: none; 9 | text-rendering: optimizeLegibility; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | -webkit-text-size-adjust: 100%; 13 | } 14 | 15 | * { 16 | box-sizing: border-box; 17 | } 18 | 19 | html, 20 | body { 21 | margin: 0; 22 | } 23 | 24 | #app { 25 | display: flex; 26 | place-items: center; 27 | min-width: 320px; 28 | min-height: 100vh; 29 | } 30 | 31 | :not(:defined) { 32 | visibility: hidden; 33 | } 34 | 35 | @media (prefers-color-scheme: light) { 36 | :root { 37 | color: rgb(33 53 71); 38 | background-color: rgb(255 255 255); 39 | } 40 | } 41 | 42 | @media (prefers-color-scheme: dark) { 43 | :root { 44 | color: rgb(255 255 255 / 87%); 45 | background-color: rgb(36 36 36); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/minze/test/lib/template/logos/minze-logo.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeLogo', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('attrs', async ({ page }) => { 10 | await expect(page.locator('minze-logo')).toHaveAttribute('width', '347') 11 | await expect(page.locator('minze-logo')).toHaveAttribute('height', '146') 12 | }) 13 | 14 | test('html', async ({ page }) => { 15 | await expect(page.locator('minze-logo svg')).toBeVisible() 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/minze/test/lib/template/logos/minze-logo.ts: -------------------------------------------------------------------------------- 1 | import type { Attrs } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MinzeLogo { 5 | width: string 6 | height: string 7 | } 8 | 9 | export class MinzeLogo extends MinzeElement { 10 | attrs: Attrs = [ 11 | ['width', 347], 12 | ['height', 146] 13 | ] 14 | 15 | static observedAttributes = ['width', 'height'] 16 | 17 | html = () => ` 18 | 19 | ` 20 | 21 | css = () => ` 22 | :host { 23 | display: inline-block; 24 | } 25 | ` 26 | } 27 | -------------------------------------------------------------------------------- /packages/minze/test/lib/template/logos/vite-logo.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('ViteLogo', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('attrs', async ({ page }) => { 10 | await expect(page.locator('vite-logo')).toHaveAttribute('width', '256') 11 | await expect(page.locator('vite-logo')).toHaveAttribute('height', '257') 12 | }) 13 | 14 | test('html', async ({ page }) => { 15 | await expect(page.locator('vite-logo svg')).toBeVisible() 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/minze/test/lib/template/logos/vite-logo.ts: -------------------------------------------------------------------------------- 1 | import type { Attrs } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface ViteLogo { 5 | width: string 6 | height: string 7 | } 8 | 9 | export class ViteLogo extends MinzeElement { 10 | attrs: Attrs = [ 11 | ['width', 256], 12 | ['height', 257] 13 | ] 14 | 15 | static observedAttributes = ['width', 'height'] 16 | 17 | html = () => ` 18 | 19 | ` 20 | 21 | css = () => ` 22 | :host { 23 | display: inline-block; 24 | } 25 | ` 26 | } 27 | -------------------------------------------------------------------------------- /packages/minze/test/lib/template/my-button.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MyButton', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('html', async ({ page }) => { 10 | await expect(page.locator('my-button button')).toBeVisible() 11 | }) 12 | 13 | test('css', async ({ page }) => { 14 | await expect(page.locator('my-button button')).toHaveCSS( 15 | 'border-radius', 16 | '8px' 17 | ) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/minze/test/lib/template/my-button.ts: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MyButton extends MinzeElement { 4 | html = () => ` 5 | 8 | ` 9 | 10 | css = () => ` 11 | button { 12 | display: inline-block; 13 | border-radius: 8px; 14 | border: 1px solid transparent; 15 | padding: 0.6em 1.2em; 16 | font-size: 1em; 17 | font-weight: 500; 18 | font-family: inherit; 19 | color: currentColor; 20 | background-color: rgb(249 249 249); 21 | cursor: pointer; 22 | user-select: none; 23 | position: relative; 24 | transition: border-color 0.25s; 25 | } 26 | 27 | button:hover { 28 | border-color: var(--color-primary, rgb(55 245 220)); 29 | } 30 | 31 | button:active { 32 | outline: 3px solid var(--color-primary, rgb(55 245 220)); 33 | outline-offset: -2px; 34 | } 35 | 36 | @media (prefers-color-scheme: dark) { 37 | button { 38 | color: currentColor; 39 | background-color: rgb(26 26 26); 40 | } 41 | } 42 | ` 43 | } 44 | -------------------------------------------------------------------------------- /packages/minze/test/lib/template/my-element.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MyElement', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '

Minze + Vite

') 7 | }) 8 | 9 | test('reactive', async ({ page }) => { 10 | const el = page.locator('my-button') 11 | await expect(el).toContainText('count is 0') 12 | el.click() 13 | await expect(el).toContainText('count is 1') 14 | }) 15 | 16 | test('html', async ({ page }) => { 17 | await expect(page.locator('h1')).toBeVisible() 18 | await expect(page.locator('h1')).toHaveText('Minze + Vite') 19 | await expect(page.locator('minze-logo svg')).toBeVisible() 20 | await expect(page.locator('vite-logo svg')).toBeVisible() 21 | await expect(page.locator('my-button button')).toBeVisible() 22 | }) 23 | 24 | test('css', async ({ page }) => { 25 | await expect(page.locator('my-element')).toHaveCSS('max-width', '1280px') 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/minze/test/lib/template/my-element.ts: -------------------------------------------------------------------------------- 1 | import type { Reactive } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MyElement { 5 | count: number 6 | } 7 | 8 | export class MyElement extends MinzeElement { 9 | reactive: Reactive = [['count', 0]] 10 | 11 | increaseCount = () => this.count++ 12 | 13 | text = 'Click on the Minze and Vite logos to learn more' 14 | 15 | html = () => ` 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 |
29 | 30 | count is ${this.count} 31 | 32 |
33 | 34 |

35 | ${this.text} 36 |

37 | ` 38 | 39 | css = () => ` 40 | :host { 41 | max-width: 1280px; 42 | margin: 0 auto; 43 | padding: 2rem; 44 | text-align: center; 45 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 46 | color: rgb(33 53 71); 47 | background-color: rgb(255 255 255); 48 | } 49 | 50 | @media (prefers-color-scheme: dark) { 51 | :host { 52 | color: rgb(255 255 255 / 87%); 53 | background-color: rgb(36 36 36); 54 | } 55 | } 56 | 57 | a { 58 | text-decoration: none; 59 | } 60 | 61 | .logo { 62 | padding: 1.5em; 63 | will-change: filter; 64 | transition: filter 300ms; 65 | } 66 | 67 | .logo:hover { 68 | filter: drop-shadow(0 0 2em rgb(50 255 160 / 65%)); 69 | } 70 | 71 | .logo.vite:hover { 72 | filter: drop-shadow(0 0 2em rgb(100 108 255 / 65%)); 73 | } 74 | 75 | .card { 76 | padding: 2em; 77 | } 78 | 79 | .text { 80 | color: rgb(136 136 136); 81 | margin: 1rem 0; 82 | } 83 | 84 | ::slotted(h1) { 85 | font-size: 3.2em; 86 | line-height: 1.1; 87 | transition: rotate 750ms; 88 | ${this.count >= 5 ? 'rotate: 360deg;' : ''} 89 | } 90 | ` 91 | } 92 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/events/minze-event-listeners.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeEventListeners', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('click (dispatch)', async ({ page }) => { 10 | await expect(page.locator('.dispatch')).toHaveText('not-clicked') 11 | await page.click('.dispatch') 12 | await expect(page.locator('.dispatch')).toHaveText('clicked') 13 | }) 14 | 15 | test('click (broadcast)', async ({ page }) => { 16 | await expect(page.locator('.broadcast')).toHaveText('not-clicked') 17 | await page.click('.broadcast') 18 | await expect(page.locator('.broadcast')).toHaveText('clicked') 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/events/minze-event-listeners.ts: -------------------------------------------------------------------------------- 1 | import type { EventListeners } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export class MinzeEventListeners extends MinzeElement { 5 | textDispatch = 'not-clicked' 6 | textBroadcast = 'not-clicked' 7 | 8 | html = () => ` 9 | 12 | 13 | 16 | ` 17 | 18 | handleClickDispatch = () => { 19 | this.dispatch('minze:click', 'clicked') 20 | } 21 | 22 | handleClickBroadcast = () => { 23 | new BroadcastChannel('$').postMessage('clicked') 24 | } 25 | 26 | handleDispatch(event: CustomEvent) { 27 | this.textDispatch = event.detail 28 | this.rerender() 29 | } 30 | 31 | handleBroadcast(event: MessageEvent) { 32 | this.textBroadcast = event.data 33 | this.rerender() 34 | } 35 | 36 | eventListeners: EventListeners = [ 37 | ['.dispatch', 'click', this.handleClickDispatch], 38 | ['.broadcast', 'click', this.handleClickBroadcast], 39 | [window, 'minze:click', this.handleDispatch], 40 | [new BroadcastChannel('$'), 'message', this.handleBroadcast] 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/events/minze-on-events.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeOnEvents', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('click (on:)', async ({ page }) => { 10 | await expect(page.locator('.on')).toHaveText('not-clicked') 11 | await page.click('.on') 12 | await expect(page.locator('.on')).toHaveText('clicked') 13 | }) 14 | 15 | test('click (@)', async ({ page }) => { 16 | await expect(page.locator('.at')).toHaveText('not-clicked') 17 | await page.click('.at') 18 | await expect(page.locator('.at')).toHaveText('clicked') 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/events/minze-on-events.ts: -------------------------------------------------------------------------------- 1 | import type { Reactive } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MinzeOnEvents { 5 | text: string 6 | } 7 | 8 | export class MinzeOnEvents extends MinzeElement { 9 | reactive: Reactive = [['text', 'not-clicked']] 10 | 11 | click = () => (this.text = 'clicked') 12 | 13 | html = () => ` 14 | 17 | 18 | 21 | ` 22 | } 23 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/minze-hooks.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeHooks', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('hooks (common)', async ({ page }) => { 10 | const hookSequence = [ 11 | 'onStart', 12 | 'onReactive', 13 | 'beforeRender', 14 | 'afterRender', 15 | 'onReady' 16 | ].join(' ') 17 | 18 | await expect(page.locator('minze-hooks div')).toHaveText(hookSequence) 19 | }) 20 | 21 | test('hooks (destroy)', async ({ page }) => { 22 | const hookSequence = [ 23 | 'onDestroy', 24 | 'onStart', 25 | 'onReactive', 26 | 'beforeRender', 27 | 'afterRender', 28 | 'onReady' 29 | ].join(' ') 30 | 31 | // move the element within the same document 32 | await page.locator('#app').evaluate((node) => { 33 | const el = node.querySelector('minze-hooks') 34 | if (el) node.appendChild(el) 35 | }) 36 | 37 | await expect(page.locator('minze-hooks div')).toHaveText(hookSequence) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/minze-hooks.ts: -------------------------------------------------------------------------------- 1 | import type { Attrs } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export class MinzeHooks extends MinzeElement { 5 | attrs: Attrs = ['hooks'] 6 | 7 | static observedAttributes = ['hooks'] 8 | 9 | text = '' 10 | 11 | html = () => ` 12 |
13 | ${this.text} 14 |
15 | ` 16 | 17 | onStart() { 18 | this.text += 'onStart ' 19 | } 20 | 21 | onReactive() { 22 | this.text += 'onReactive ' 23 | } 24 | 25 | onReady() { 26 | this.text += 'onReady ' 27 | this.rerender() 28 | } 29 | 30 | onDestroy() { 31 | this.text = 'onDestroy ' 32 | } 33 | 34 | beforeRender() { 35 | this.text += 'beforeRender ' 36 | } 37 | 38 | afterRender() { 39 | this.text += 'afterRender ' 40 | } 41 | 42 | beforeAttributeChange() { 43 | this.text += 'beforeAttributeChange ' 44 | } 45 | 46 | afterAttributeChange() { 47 | this.text += 'afterAttributeChange ' 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/minze-options.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeOptions', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('cssReset', async ({ page }) => { 10 | await expect(page.locator('h1')).toHaveCSS('margin', '0px') 11 | }) 12 | 13 | test('[rendered]', async ({ page }) => { 14 | await expect(page.locator('minze-options')).toHaveAttribute('rendered', '') 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/minze-options.ts: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MinzeOptions extends MinzeElement { 4 | options = { 5 | cssReset: true, 6 | exposeAttrs: { 7 | rendered: true 8 | } 9 | } 10 | 11 | html = () => `

Headline

` 12 | } 13 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/reactivity/minze-attrs.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeAttrs', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('types', async ({ page }) => { 10 | await expect(page.locator('.count')).toHaveText('0') 11 | await expect(page.locator('minze-attrs')).toHaveAttribute('count', '0') 12 | 13 | await expect(page.locator('.shorthand')).toHaveText('object') // null object 14 | await expect(page.locator('minze-attrs')).not.toHaveAttribute( 15 | 'shorthand', 16 | 'null' 17 | ) 18 | 19 | await expect(page.locator('.empty')).toHaveText('object') // null object 20 | await expect(page.locator('minze-attrs')).not.toHaveAttribute( 21 | 'empty', 22 | 'null' 23 | ) 24 | 25 | await expect(page.locator('.undefined')).toHaveText('undefined') 26 | await expect(page.locator('minze-attrs')).toHaveAttribute( 27 | 'undefined', 28 | 'undefined' 29 | ) 30 | 31 | await expect(page.locator('.null')).toHaveText('object') // null object 32 | await expect(page.locator('minze-attrs')).toHaveAttribute('null', 'null') 33 | 34 | await expect(page.locator('.boolean')).toHaveText('boolean') 35 | await expect(page.locator('minze-attrs')).toHaveAttribute( 36 | 'boolean', 37 | 'false' 38 | ) 39 | 40 | await expect(page.locator('.int')).toHaveText('number') 41 | await expect(page.locator('minze-attrs')).toHaveAttribute('int', '1') 42 | 43 | await expect(page.locator('.float')).toHaveText('number') 44 | await expect(page.locator('minze-attrs')).toHaveAttribute('float', '1.1') 45 | 46 | await expect(page.locator('.object')).toHaveText('object') 47 | await expect(page.locator('minze-attrs')).toHaveAttribute( 48 | 'object', 49 | '{"key":"value"}' 50 | ) 51 | }) 52 | 53 | test('reactivity', async ({ page }) => { 54 | await expect(page.locator('.count')).toHaveText('0') 55 | await expect(page.locator('minze-attrs')).toHaveAttribute('count', '0') 56 | 57 | await page.locator('minze-attrs').evaluate((node) => { 58 | node.setAttribute('count', '1') 59 | }) 60 | 61 | await expect(page.locator('.count')).toHaveText('1') 62 | await expect(page.locator('minze-attrs')).toHaveAttribute('count', '1') 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/reactivity/minze-attrs.ts: -------------------------------------------------------------------------------- 1 | import { MinzeElement, Attrs } from 'minze' 2 | 3 | export interface MinzeAttrs { 4 | count: string 5 | shorthand: null 6 | empty: null 7 | undefined: undefined 8 | null: null 9 | boolean: boolean 10 | int: number 11 | float: number 12 | object: Record 13 | } 14 | 15 | export class MinzeAttrs extends MinzeElement { 16 | attrs: Attrs = [ 17 | ['count', 0], 18 | 'shorthand', 19 | ['empty'], 20 | ['undefined', undefined], 21 | ['null', null], 22 | ['boolean', false], 23 | ['int', 1], 24 | ['float', 1.1], 25 | ['object', { key: 'value' }] 26 | ] 27 | 28 | static observedAttributes = ['count'] 29 | 30 | html = () => ` 31 |
32 | ${this.count} 33 |
34 | 35 |
36 | ${typeof this.shorthand} 37 |
38 | 39 |
40 | ${typeof this.empty} 41 |
42 | 43 |
44 | ${typeof this.undefined} 45 |
46 | 47 |
48 | ${typeof this.null} 49 |
50 | 51 |
52 | ${typeof this.boolean} 53 |
54 | 55 |
56 | ${typeof this.int} 57 |
58 | 59 |
60 | ${typeof this.float} 61 |
62 | 63 |
64 | ${typeof this.object} 65 |
66 | ` 67 | } 68 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/reactivity/minze-patch.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzePatch', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('html (patch)', async ({ page }) => { 10 | await page.fill('input', '99') 11 | await expect(page.locator('minze-patch')).toBeFocused() 12 | }) 13 | 14 | test('html (re-render)', async ({ page }) => { 15 | await page.fill('input', '99') 16 | await page.click('button') 17 | await expect(page.locator('minze-patch')).not.toBeFocused() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/reactivity/minze-patch.ts: -------------------------------------------------------------------------------- 1 | import type { Reactive } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MinzePatch { 5 | value: number 6 | } 7 | 8 | export class MinzePatch extends MinzeElement { 9 | reactive: Reactive = [['value', 0]] 10 | 11 | keyup = (event: KeyboardEvent) => { 12 | this.value = parseInt((event.target as HTMLInputElement).value) 13 | } 14 | 15 | click = () => { 16 | this.rerender() 17 | } 18 | 19 | html = () => ` 20 | 21 | 22 | ` 23 | } 24 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/reactivity/minze-reactive.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeReactive', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('reactivity', async ({ page }) => { 10 | await expect(page.locator('.shorthand')).toHaveText('null') 11 | await expect(page.locator('.str')).toHaveText('initial text') 12 | await expect(page.locator('.arr')).toHaveText('1,2,3') 13 | await expect(page.locator('.obj')).toHaveText('initial value') 14 | 15 | await expect(page.locator('minze-reactive')).not.toHaveAttribute( 16 | 'shorthand', 17 | 'null' 18 | ) 19 | await expect(page.locator('minze-reactive')).toHaveAttribute( 20 | 'str', 21 | 'initial text' 22 | ) 23 | await expect(page.locator('minze-reactive')).toHaveAttribute( 24 | 'arr', 25 | '[1,2,3]' 26 | ) 27 | await expect(page.locator('minze-reactive')).toHaveAttribute( 28 | 'obj', 29 | '{"nested":{"prop":"initial value"}}' 30 | ) 31 | 32 | await page.click('button') 33 | 34 | await expect(page.locator('.shorthand')).toHaveText('not null') 35 | await expect(page.locator('.str')).toHaveText('changed text') 36 | await expect(page.locator('.arr')).toHaveText('1,2,3,4') 37 | await expect(page.locator('.obj')).toHaveText('changed value') 38 | 39 | await expect(page.locator('.shorthand')).not.toHaveAttribute( 40 | 'shorthand', 41 | 'not null' 42 | ) 43 | await expect(page.locator('minze-reactive')).toHaveAttribute( 44 | 'str', 45 | 'changed text' 46 | ) 47 | await expect(page.locator('minze-reactive')).toHaveAttribute( 48 | 'arr', 49 | '[1,2,3,4]' 50 | ) 51 | await expect(page.locator('minze-reactive')).toHaveAttribute( 52 | 'obj', 53 | '{"nested":{"prop":"changed value"}}' 54 | ) 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/reactivity/minze-reactive.ts: -------------------------------------------------------------------------------- 1 | import type { Reactive } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MinzeReactive { 5 | shorthand: string | null 6 | str: string 7 | arr: number[] 8 | obj: { 9 | nested: { 10 | prop: string 11 | } 12 | } 13 | } 14 | 15 | export class MinzeReactive extends MinzeElement { 16 | reactive: Reactive = [ 17 | 'shorthand', 18 | ['str', 'initial text', true], 19 | ['arr', [1, 2, 3], true], 20 | [ 21 | 'obj', 22 | { 23 | nested: { 24 | prop: 'initial value' 25 | } 26 | }, 27 | true 28 | ] 29 | ] 30 | 31 | click = () => { 32 | this.shorthand = 'not null' 33 | this.str = 'changed text' 34 | this.arr.push(4) 35 | this.obj.nested.prop = 'changed value' 36 | } 37 | 38 | html = () => ` 39 |
${this.shorthand}
40 |
${this.str}
41 |
${this.arr}
42 |
${this.obj.nested.prop}
43 | 44 | ` 45 | } 46 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/reactivity/minze-watch.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeWatch', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('reactivity', async ({ page }) => { 10 | await expect(page.locator('.str')).toHaveText('') 11 | await expect(page.locator('.arr')).toHaveText('') 12 | await expect(page.locator('.obj')).toHaveText('') 13 | 14 | await page.click('button') 15 | 16 | await expect(page.locator('.str')).toHaveText( 17 | 'changed text, initial text, str, [object HTMLElement]' 18 | ) 19 | await expect(page.locator('.arr')).toHaveText('4, undefined, 3, 1,2,3,4') 20 | await expect(page.locator('.obj')).toHaveText( 21 | 'changed value, initial value, prop, [object Object]' 22 | ) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/reactivity/minze-watch.ts: -------------------------------------------------------------------------------- 1 | import type { Reactive, Watch } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MinzeWatch { 5 | str: string 6 | arr: number[] 7 | obj: { 8 | nested: { 9 | prop: string 10 | } 11 | } 12 | } 13 | 14 | export class MinzeWatch extends MinzeElement { 15 | reactive: Reactive = [ 16 | ['str', 'initial text'], 17 | ['arr', [1, 2, 3]], 18 | [ 19 | 'obj', 20 | { 21 | nested: { 22 | prop: 'initial value' 23 | } 24 | } 25 | ] 26 | ] 27 | 28 | watch: Watch = [ 29 | [ 30 | 'str', 31 | (newValue, oldValue, key, target) => { 32 | const el = this.select('.str') 33 | if (el) el.innerHTML = `${newValue}, ${oldValue}, ${key}, ${target}` 34 | } 35 | ], 36 | [ 37 | 'arr', 38 | (newValue, oldValue, key, target) => { 39 | const el = this.select('.arr') 40 | if (el) el.innerHTML = `${newValue}, ${oldValue}, ${key}, ${target}` 41 | } 42 | ], 43 | [ 44 | 'obj', 45 | (newValue, oldValue, key, target) => { 46 | const el = this.select('.obj') 47 | if (el) el.innerHTML = `${newValue}, ${oldValue}, ${key}, ${target}` 48 | } 49 | ] 50 | ] 51 | 52 | click = () => { 53 | this.str = 'changed text' 54 | this.arr.push(4) 55 | this.obj.nested.prop = 'changed value' 56 | } 57 | 58 | html = () => ` 59 |
60 |
61 |
62 | 63 | ` 64 | } 65 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/template/minze-exportparts.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeExportparts', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, '') 7 | }) 8 | 9 | test('exportparts (static)', async ({ page }) => { 10 | await expect(page.locator('minze-exportparts')).toHaveAttribute( 11 | 'exportparts', 12 | 'button, button--primary' 13 | ) 14 | }) 15 | 16 | test('exportparts (dynamic)', async ({ page }) => { 17 | await page.click('button') 18 | 19 | await expect(page.locator('minze-exportparts')).toHaveAttribute( 20 | 'exportparts', 21 | 'another-part, button, button--primary' 22 | ) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/template/minze-exportparts.ts: -------------------------------------------------------------------------------- 1 | import type { Reactive } from 'minze' 2 | import { MinzeElement } from 'minze' 3 | 4 | export interface MinzeExportparts { 5 | clicked: boolean 6 | } 7 | 8 | export class MinzeExportparts extends MinzeElement { 9 | options = { exposeAttrs: { exportparts: true } } 10 | 11 | reactive: Reactive = [['clicked', false]] 12 | 13 | click = () => (this.clicked = true) 14 | 15 | html = () => ` 16 | 23 | ` 24 | } 25 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/template/minze-selectors.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeSelectors', () => { 5 | test.beforeEach(async ({ page }) => { 6 | const html = ` 7 | 8 |

9 |

10 |
11 | ` 12 | 13 | await setup(page, html) 14 | }) 15 | 16 | test('select', async ({ page }) => { 17 | await expect(page.locator('.div-1')).toHaveClass('div-1 selected') 18 | }) 19 | 20 | test('selectAll', async ({ page }) => { 21 | await expect(page.locator('.div-1')).toHaveClass('div-1 selected') 22 | await expect(page.locator('.div-2')).toHaveClass('div-2 selected') 23 | }) 24 | 25 | test('slotted', async ({ page }) => { 26 | await expect(page.locator('h1')).toHaveClass('named-slot') 27 | await expect(page.locator('p')).toHaveClass('default-slot') 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/template/minze-selectors.ts: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MinzeSelectors extends MinzeElement { 4 | html = () => ` 5 |
6 |
7 | 8 | 9 | ` 10 | 11 | onReady() { 12 | this.select('.div-1')?.classList.add('selected') 13 | this.selectAll('div')?.forEach((el) => el.classList.add('selected')) 14 | this.slotted('named-slot')?.[0].classList.add('named-slot') 15 | this.slotted('default')?.[0].classList.add('default-slot') 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/template/minze-templating.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test' 2 | import { setup } from '@/utils' 3 | 4 | test.describe('MinzeTemplating', () => { 5 | test.beforeEach(async ({ page }) => { 6 | await setup(page, 'Hello Minze!') 7 | }) 8 | 9 | test('html', async ({ page }) => { 10 | await expect(page.locator('minze-templating')).toContainText('Hello Minze!') 11 | }) 12 | 13 | test('css', async ({ page }) => { 14 | await expect(page.locator('minze-templating')).toHaveCSS( 15 | 'box-sizing', 16 | 'border-box' 17 | ) 18 | await expect(page.locator('minze-templating')).toHaveCSS('display', 'block') 19 | await expect(page.locator('minze-templating')).toHaveCSS( 20 | 'background-color', 21 | 'rgb(55, 245, 220)' 22 | ) 23 | }) 24 | 25 | test('[hidden]', async ({ page }) => { 26 | await page.locator('minze-templating').evaluate((node) => { 27 | node.setAttribute('hidden', '') 28 | }) 29 | 30 | await expect(page.locator('minze-templating')).toHaveCSS('display', 'none') 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /packages/minze/test/lib/testing/template/minze-templating.ts: -------------------------------------------------------------------------------- 1 | import { MinzeElement } from 'minze' 2 | 3 | export class MinzeTemplating extends MinzeElement { 4 | html = () => ` 5 | 6 | ` 7 | 8 | css = () => ` 9 | :host { 10 | background: var(--color-primary, rgb(55 245 220)); 11 | } 12 | ` 13 | } 14 | -------------------------------------------------------------------------------- /packages/minze/test/main.ts: -------------------------------------------------------------------------------- 1 | import { Minze } from 'minze' 2 | const modules = import.meta.glob('./lib/**/!(*.spec|*.test|*.stories).@(ts|js)') 3 | const defineAll = Minze.defineAll 4 | 5 | export { modules as default, modules, defineAll } 6 | -------------------------------------------------------------------------------- /packages/minze/test/preview.html: -------------------------------------------------------------------------------- 1 | 2 |

Minze + Vite

3 |
4 | -------------------------------------------------------------------------------- /packages/minze/test/types/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/minze/test/utils.ts: -------------------------------------------------------------------------------- 1 | import type { Page } from '@playwright/test' 2 | 3 | /** 4 | * Navigates to '/' and injects provided markup into the DOM. 5 | * 6 | * @param page - Playwright page object. 7 | * @param html - A string with HTML markup. 8 | * 9 | * @example 10 | * import { test, expect } from '@playwright/test' 11 | * import { setup } from '@/utils' 12 | * 13 | * test.describe('MyElement', () => { 14 | * test.beforeEach(async ({ page }) => { 15 | * await setup(page, '') 16 | * }) 17 | * 18 | * test('html', async ({ page }) => { 19 | * await expect(page.locator('my-element')).toBeVisible() 20 | * }) 21 | * }) 22 | */ 23 | export async function setup(page: Page, html: string) { 24 | await page.goto('/') 25 | 26 | await page.evaluate((html) => { 27 | const app = document.querySelector('#app') 28 | if (app) app.innerHTML = html 29 | }, html) 30 | } 31 | -------------------------------------------------------------------------------- /packages/minze/test/vite.ts: -------------------------------------------------------------------------------- 1 | import './assets/vite.css' 2 | 3 | import { modules, defineAll } from './main' 4 | defineAll(modules) 5 | 6 | if (import.meta.env.MODE !== 'testing') { 7 | const previews = import.meta.glob('./*.html', { 8 | eager: true, 9 | query: '?raw', 10 | import: 'default' 11 | }) 12 | const preview = previews['./preview.dev.html'] ?? previews['./preview.html'] 13 | 14 | const app = document.querySelector('#app') 15 | if (app) app.innerHTML = preview 16 | } 17 | -------------------------------------------------------------------------------- /packages/minze/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "allowJs": false, 7 | "strict": true, 8 | "noImplicitAny": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "skipLibCheck": true, 11 | "baseUrl": ".", 12 | "paths": { 13 | "minze": ["./src/main.ts"], 14 | "src/*": ["./src/*"], 15 | "@/*": ["./test/*"] 16 | } 17 | }, 18 | "include": ["./src", "./test"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/minze/vite.config.build.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import pkg from './package.json' 3 | import dts from 'vite-plugin-dts' 4 | 5 | export default defineConfig({ 6 | define: { 7 | __VERSION__: JSON.stringify(pkg.version) 8 | }, 9 | resolve: { 10 | alias: { src: new URL('./src', import.meta.url).pathname } 11 | }, 12 | esbuild: { 13 | keepNames: true 14 | }, 15 | build: { 16 | emptyOutDir: true, 17 | copyPublicDir: false, 18 | lib: { 19 | name: 'minze', 20 | formats: ['es'], 21 | entry: 'src/main.ts', 22 | fileName: () => 'main.js' 23 | }, 24 | rollupOptions: { 25 | output: { 26 | inlineDynamicImports: true, 27 | minifyInternalExports: false 28 | } 29 | } 30 | }, 31 | plugins: [ 32 | dts({ 33 | rollupTypes: true, 34 | entryRoot: 'src', 35 | include: ['src'] 36 | }) 37 | ] 38 | }) 39 | -------------------------------------------------------------------------------- /packages/minze/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { defineConfig } from 'vite' 4 | import { fileURLToPath } from 'node:url' 5 | import pkg from './package.json' 6 | import minze from 'vite-plugin-minze' 7 | import dts from 'vite-plugin-dts' 8 | 9 | export default defineConfig({ 10 | test: { 11 | include: ['src/**/*.test.*'], 12 | environment: 'happy-dom', 13 | onConsoleLog(log) { 14 | if (log.includes('Minze')) return false 15 | } 16 | }, 17 | define: { 18 | __VERSION__: JSON.stringify(pkg.version) 19 | }, 20 | resolve: { 21 | alias: { 22 | minze: fileURLToPath(new URL('./src/main.ts', import.meta.url)), 23 | src: new URL('./src', import.meta.url).pathname, 24 | '@': new URL('./test', import.meta.url).pathname 25 | } 26 | }, 27 | plugins: [ 28 | minze({ entry: 'test/main.ts' }), 29 | dts({ 30 | entryRoot: 'test', 31 | include: ['test', 'src/env.d.ts'], 32 | exclude: ['test/{vite,utils}.ts', 'test/**/*.{spec,test,stories}.ts'] 33 | }) 34 | ] 35 | }) 36 | -------------------------------------------------------------------------------- /packages/vite-plugin-minze/.bumpfile: -------------------------------------------------------------------------------- 1 | 17915 2 | -------------------------------------------------------------------------------- /packages/vite-plugin-minze/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023-present, Sergej Samsonenko 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /packages/vite-plugin-minze/README.md: -------------------------------------------------------------------------------- 1 | # vite-plugin-minze 2 | 3 | Vite plugin for Minze dev environment. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | npm add -D vite-plugin-minze 9 | ``` 10 | 11 | ```js 12 | // vite.config.js 13 | import { defineConfig } from 'vite' 14 | import minze from 'vite-plugin-minze' 15 | 16 | export default defineConfig({ 17 | plugins: [ 18 | minze({ 19 | entry: 'src/main.js' // default path is 'src/main.js' or 'src/main.ts' for TypeScript projects. 20 | }) 21 | ] 22 | }) 23 | ``` 24 | -------------------------------------------------------------------------------- /packages/vite-plugin-minze/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild' 2 | 3 | export default defineBuildConfig({ 4 | clean: true, 5 | externals: ['vite'] 6 | }) 7 | -------------------------------------------------------------------------------- /packages/vite-plugin-minze/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-plugin-minze", 3 | "version": "1.1.6", 4 | "license": "MIT", 5 | "author": "Sergej Samsonenko", 6 | "description": "Vite plugin for Minze dev environment.", 7 | "keywords": [ 8 | "vite-plugin", 9 | "minze", 10 | "minzejs" 11 | ], 12 | "type": "module", 13 | "main": "dist/main.mjs", 14 | "types": "dist/main.d.ts", 15 | "files": [ 16 | "dist" 17 | ], 18 | "engines": { 19 | "node": ">=v16.0.0" 20 | }, 21 | "homepage": "https://github.com/sergejcodes/minze/tree/main/packages/vite-plugin-minze#readme", 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/sergejcodes/minze.git", 25 | "directory": "packages/vite-plugin-minze" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/sergejcodes/minze/issues" 29 | }, 30 | "scripts": { 31 | "stub": "unbuild --stub", 32 | "build": "unbuild", 33 | "test": "(npm run build) && (npm run build-test -w minze)", 34 | "posttest": "(npm run stub) && (npm run build -w minze)" 35 | }, 36 | "devDependencies": { 37 | "typescript": "^5.8.3", 38 | "unbuild": "^3.5.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/vite-plugin-minze/src/main.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from 'vite' 2 | import { existsSync } from 'node:fs' 3 | import { normalize, join } from 'node:path' 4 | 5 | interface PluginOptions { 6 | entry?: string 7 | } 8 | 9 | interface Context { 10 | command: 'build' | 'serve' 11 | mode: string 12 | } 13 | 14 | /** 15 | * vite-plugin-minze 16 | * 17 | * @example 18 | * ``` 19 | * import { defineConfig } from 'vite' 20 | * import minze from 'vite-plugin-minze' 21 | * 22 | * export default defineConfig({ 23 | * plugins: [ 24 | * minze({ entry: 'src/main.js' }), 25 | * // ... 26 | * ] 27 | * }) 28 | * ``` 29 | */ 30 | export default (options?: PluginOptions): Plugin => { 31 | const isTypeScript = existsSync(join(process.cwd(), 'tsconfig.json')) 32 | const entry = options?.entry ?? `src/main.${isTypeScript ? 'ts' : 'js'}` 33 | 34 | return { 35 | name: 'vite-plugin-minze', 36 | config: (config, { command }: Context) => { 37 | // prevent overriding storybook config 38 | const isStorybook = 39 | config.define?.['import.meta.env.STORYBOOK'] || 40 | config.envPrefix?.includes('STORYBOOK_') 41 | 42 | // keep original class names 43 | if (command === 'serve' || isStorybook) { 44 | return { 45 | esbuild: { 46 | keepNames: true 47 | } 48 | } 49 | } 50 | 51 | if (command === 'build') { 52 | return { 53 | esbuild: { 54 | keepNames: true 55 | }, 56 | build: { 57 | emptyOutDir: true, 58 | lib: { 59 | name: 'minze', 60 | formats: ['es'], 61 | entry, 62 | fileName: () => 'main.js' 63 | }, 64 | rollupOptions: { 65 | output: { 66 | inlineDynamicImports: false, 67 | minifyInternalExports: false, 68 | chunkFileNames: '[name].js', 69 | manualChunks: (id, match) => { 70 | const isIncluded = match.getModuleInfo(id)?.isIncluded 71 | const idIcludesCWD = normalize(id).includes( 72 | normalize(process.cwd()) 73 | ) 74 | 75 | if (isIncluded && id.includes('/node_modules/')) { 76 | const pathRE = /(?<=node_modules\/).*(?=\.(?:ts|m?js))/i 77 | const match = id.match(pathRE)?.[0] 78 | 79 | if (match) return `vendor/${match}` 80 | } else if ( 81 | isIncluded && 82 | idIcludesCWD && 83 | id.includes('/lib/') 84 | ) { 85 | const pathRE = /(?<=lib\/).*(?=\.(?:ts|m?js))/i 86 | const match = id.match(pathRE)?.[0] 87 | 88 | if (match) return `lib/${match}` 89 | } else if (isIncluded) { 90 | const pathRE = /(?:\/)([\w\-+. ]+)(?:\..*)$/i 91 | const match = id.match(pathRE)?.[1] 92 | 93 | if (match) return `chunks/${match}` 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packages/vite-plugin-minze/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "allowJs": false, 7 | "strict": true, 8 | "noImplicitAny": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "skipLibCheck": true 11 | }, 12 | "include": ["./src"] 13 | } 14 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", 3 | "separate-pull-requests": true, 4 | "release-type": "node", 5 | "changelog-sections": [ 6 | { "type": "feat", "section": "Features" }, 7 | { "type": "fix", "section": "Bug Fixes" }, 8 | { "type": "release", "section": "Miscellaneous" } 9 | ], 10 | "packages": { 11 | "packages/create-minze": {}, 12 | "packages/minze": {}, 13 | "packages/minze-vsc": {}, 14 | "packages/vite-plugin-minze": {} 15 | } 16 | } 17 | --------------------------------------------------------------------------------