├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── build.yml
├── .gitignore
├── .npmrc
├── .vscode
├── extensions.json
└── settings.json
├── LICENSE
├── Procfile.dev
├── README.md
├── docs
├── .algolia
│ └── config.json
├── .gitignore
├── README.md
├── composables.d.ts
├── cypress.config.js
├── cypress
│ ├── e2e
│ │ ├── dark-mode.cypress.cy.js
│ │ ├── docsearch.cypress.cy.js
│ │ ├── helpers.js
│ │ ├── home.cypress.cy.js
│ │ └── sidebar.cypress.cy.js
│ ├── fixtures
│ │ └── example.json
│ └── support
│ │ ├── commands.js
│ │ └── e2e.js
├── icons
│ ├── arrowRight.svg
│ ├── astro.svg
│ ├── cloudflare.svg
│ ├── logo.svg
│ ├── netlify.svg
│ ├── text.svg
│ ├── vercel.svg
│ ├── vite.svg
│ └── vue.svg
├── iles.config.ts
├── images
│ ├── banner.avif
│ ├── banner.jpg
│ ├── banner.png
│ ├── devtools-frameworks.png
│ ├── favicon.ico
│ ├── logo-round.png
│ ├── logo.png
│ ├── soon.jpg
│ └── stackblitz.svg
├── index.d.ts
├── modules
│ └── lastUpdated.ts
├── package.json
├── postcss.config.js
├── public
│ ├── _headers
│ ├── _redirects
│ ├── apple-touch-icon.png
│ ├── favicon.svg
│ ├── pwa-192x192.png
│ ├── pwa-512x512.png
│ └── robots.txt
├── scripts
│ └── test-cypress
├── src
│ ├── app.ts
│ ├── components
│ │ ├── AppButton.vue
│ │ ├── AstroLogo.vue
│ │ ├── AutoImported.vue
│ │ ├── Caption.vue
│ │ ├── CloudflareLogo.vue
│ │ ├── DarkModeSwitch.vue
│ │ ├── DocSearch.tsx
│ │ ├── EditLink.vue
│ │ ├── GitHubLogo.vue
│ │ ├── HomeFeatures.vue
│ │ ├── HomeHero.vue
│ │ ├── Iles.vue
│ │ ├── Image.vue
│ │ ├── LastUpdated.vue
│ │ ├── Logo.vue
│ │ ├── MainContainer.vue
│ │ ├── MetaTags.vue
│ │ ├── NavBarButton.vue
│ │ ├── NavBarLink.vue
│ │ ├── NavBarLinks.vue
│ │ ├── NavBarLogo.vue
│ │ ├── NetlifyLogo.vue
│ │ ├── NextAndPrevLinks.vue
│ │ ├── OutboundLink.vue
│ │ ├── PageFooter.vue
│ │ ├── PageHooks.mdx
│ │ ├── ReloadPrompt.vue
│ │ ├── SearchButton.vue
│ │ ├── SidebarBackground.vue
│ │ ├── SidebarHeader.vue
│ │ ├── SidebarLink.vue
│ │ ├── SidebarLinkItem.vue
│ │ ├── SidebarNav.vue
│ │ ├── SidebarToggle.vue
│ │ ├── TableOfContents.vue
│ │ ├── TheFooter.vue
│ │ ├── TheNavBar.vue
│ │ ├── TheRightSidebar.vue
│ │ ├── TheSidebar.vue
│ │ ├── TimeAgo.vue
│ │ ├── Tip.vue
│ │ ├── VercelLogo.vue
│ │ ├── ViteLogo.vue
│ │ ├── VueLogo.vue
│ │ ├── contact.mdx
│ │ └── should.mdx
│ ├── layouts
│ │ ├── base.vue
│ │ ├── default.vue
│ │ └── home.vue
│ ├── logic
│ │ ├── config.ts
│ │ ├── dark-color-scheme-check.ts
│ │ ├── dark.ts
│ │ ├── sidebar.ts
│ │ └── utils.ts
│ ├── pages
│ │ ├── 404.vue
│ │ ├── config
│ │ │ └── index.mdx
│ │ ├── dynamic
│ │ │ └── [section].vue
│ │ ├── faqs
│ │ │ ├── index.mdx
│ │ │ └── troubleshooting.mdx
│ │ ├── guide
│ │ │ ├── client-scripts.mdx
│ │ │ ├── deployment.mdx
│ │ │ ├── development.mdx
│ │ │ ├── documents.mdx
│ │ │ ├── frameworks.mdx
│ │ │ ├── hydration.mdx
│ │ │ ├── index.mdx
│ │ │ ├── introduction.mdx
│ │ │ ├── markdown.mdx
│ │ │ ├── meta-tags.mdx
│ │ │ ├── overview.mdx
│ │ │ ├── plugins.mdx
│ │ │ ├── pwa.mdx
│ │ │ ├── routing.mdx
│ │ │ ├── rss.mdx
│ │ │ └── turbo.mdx
│ │ └── index.mdx
│ ├── site.ts
│ ├── style.css
│ └── styles
│ │ ├── all.css
│ │ ├── docsearch.css
│ │ ├── global.css
│ │ ├── highlight.css
│ │ ├── navbar.css
│ │ ├── prose.css
│ │ ├── themes.css
│ │ ├── utilities.css
│ │ └── variables.css
├── tsconfig.json
└── uno.config.ts
├── eslint.config.mjs
├── iles.config.ts
├── netlify.toml
├── nx.json
├── package.json
├── packages
├── excerpt
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── excerpt.cjs
│ │ ├── excerpt.ts
│ │ ├── recma-plugin.ts
│ │ ├── rehype-plugin.ts
│ │ └── types.ts
│ └── tsup.config.ts
├── feed
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── feed.cjs
│ │ ├── feed.ts
│ │ ├── render-feed.ts
│ │ └── types.ts
│ └── tsup.config.ts
├── headings
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── headings.cjs
│ │ └── headings.ts
│ └── tsup.config.ts
├── hydration
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── hydration.ts
│ ├── package.json
│ ├── preact.ts
│ ├── solid.ts
│ ├── svelte.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── types.ts
│ ├── vanilla.ts
│ └── vue.ts
├── icons
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── icons.mjs
│ │ └── icons.ts
│ └── tsup.config.ts
├── iles
│ ├── .gitignore
│ ├── .vscode
│ │ └── settings.json
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── bin
│ │ └── iles.js
│ ├── config.js
│ ├── index.cjs
│ ├── jsx-dev-runtime.js
│ ├── jsx-runtime.js
│ ├── package.json
│ ├── scripts
│ │ ├── copyClient.js
│ │ ├── copyShared.js
│ │ └── watchAndCopy.js
│ ├── src
│ │ ├── client
│ │ │ ├── app
│ │ │ │ ├── components
│ │ │ │ │ ├── App.vue
│ │ │ │ │ ├── DebugPanel.vue
│ │ │ │ │ ├── Island.vue
│ │ │ │ │ └── NotFound.vue
│ │ │ │ ├── composables
│ │ │ │ │ ├── appConfig.ts
│ │ │ │ │ ├── devtools.ts
│ │ │ │ │ ├── islandDefinitions.ts
│ │ │ │ │ ├── mdxComponents.ts
│ │ │ │ │ ├── pageData.ts
│ │ │ │ │ ├── reactivity.ts
│ │ │ │ │ ├── renderer.ts
│ │ │ │ │ ├── routerLinks.ts
│ │ │ │ │ └── vueRenderer.ts
│ │ │ │ ├── head.ts
│ │ │ │ ├── hydration.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── layout.ts
│ │ │ │ ├── props.ts
│ │ │ │ └── utils.ts
│ │ │ ├── client.d.ts
│ │ │ ├── index.ts
│ │ │ ├── tsconfig.json
│ │ │ └── virtual.d.ts
│ │ ├── node
│ │ │ ├── alias.ts
│ │ │ ├── build
│ │ │ │ ├── build.ts
│ │ │ │ ├── bundle.ts
│ │ │ │ ├── chunks.ts
│ │ │ │ ├── islands.ts
│ │ │ │ ├── rebaseImports.ts
│ │ │ │ ├── render.ts
│ │ │ │ ├── routes.ts
│ │ │ │ ├── sitemap.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── write.ts
│ │ │ ├── cli.ts
│ │ │ ├── config.ts
│ │ │ ├── constants.ts
│ │ │ ├── index.ts
│ │ │ ├── modules.ts
│ │ │ ├── plugin
│ │ │ │ ├── composables.ts
│ │ │ │ ├── documents.ts
│ │ │ │ ├── hmr.ts
│ │ │ │ ├── markdown.ts
│ │ │ │ ├── middleware.ts
│ │ │ │ ├── parse.ts
│ │ │ │ ├── plugin.ts
│ │ │ │ ├── remarkWrapIslands.ts
│ │ │ │ ├── site.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── wrap.ts
│ │ │ ├── preview.ts
│ │ │ ├── publicUtils.ts
│ │ │ ├── server.ts
│ │ │ └── utils.ts
│ │ └── shared
│ │ │ ├── shared.ts
│ │ │ └── tsconfig.json
│ ├── tests
│ │ ├── __snapshots__
│ │ │ ├── build.spec.ts.snap
│ │ │ └── site.spec.ts.snap
│ │ ├── app-config.spec.ts
│ │ ├── build.spec.ts
│ │ ├── exports.spec.ts
│ │ ├── layouts.spec.ts
│ │ ├── not-found.spec.ts
│ │ ├── parse.spec.ts
│ │ ├── pretty-urls.spec.ts
│ │ ├── resolvers.spec.ts
│ │ ├── routes.spec.ts
│ │ ├── site.spec.ts
│ │ ├── user-app.spec.ts
│ │ └── utils.spec.ts
│ ├── tsconfig.json
│ ├── tsup-cjs.config.ts
│ ├── tsup.config.ts
│ ├── turbo.js
│ └── types
│ │ ├── client.d.ts
│ │ ├── index.d.ts
│ │ ├── shared.d.ts
│ │ └── virtual.d.ts
├── images
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── Picture.vue
│ │ └── images.ts
│ └── tsup.config.ts
├── mdx
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── mdx-vite-plugins.ts
│ │ ├── mdx.cjs
│ │ ├── mdx.ts
│ │ ├── plugins.ts
│ │ ├── recma-plugin.ts
│ │ ├── rehype-raw-expressions.ts
│ │ ├── remark-internal-hrefs.ts
│ │ ├── remark-mdx-images.ts
│ │ ├── types.ts
│ │ └── utils.ts
│ └── tsup.config.ts
├── pages
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── api.ts
│ │ ├── frontmatter.ts
│ │ ├── hmr.ts
│ │ ├── pages.cjs
│ │ ├── pages.ts
│ │ ├── types.ts
│ │ └── utils.ts
│ └── tsup.config.ts
├── prerender
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── prerender.ts
│ ├── svelte.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
├── prism
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── prism.cjs
│ │ └── prism.ts
│ └── tsup.config.ts
└── pwa
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ ├── pwa.mjs
│ └── pwa.ts
│ └── tsup.config.ts
├── playground
└── the-vue-point
│ ├── .gitignore
│ ├── README.md
│ ├── composables.d.ts
│ ├── iles.config.ts
│ ├── index.d.ts
│ ├── package.json
│ ├── public
│ ├── _headers
│ ├── favicon.ico
│ ├── iles.svg
│ └── logo.svg
│ ├── src
│ ├── app.ts
│ ├── components
│ │ ├── Author.vue
│ │ ├── BackLink.tsx
│ │ ├── MetaTags.vue
│ │ ├── NavBarLinks.svelte
│ │ └── PostDate.vue
│ ├── images
│ │ ├── bench.png
│ │ └── one-piece.png
│ ├── layouts
│ │ ├── default.vue
│ │ └── post.vue
│ ├── logic
│ │ ├── pagination.ts
│ │ └── posts.ts
│ ├── pages
│ │ ├── 404.vue
│ │ ├── feed.vue
│ │ ├── index.vue
│ │ └── posts
│ │ │ ├── [page].vue
│ │ │ ├── hello-2021.mdx
│ │ │ ├── volar-1-0.mdx
│ │ │ ├── vue-2-7-beta.mdx
│ │ │ ├── vue-2-7-naruto.mdx
│ │ │ ├── vue-3-2.mdx
│ │ │ ├── vue-3-as-the-new-default.mdx
│ │ │ └── vue-3-one-piece.mdx
│ ├── site.ts
│ └── style.css
│ ├── tsconfig.json
│ └── uno.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── scripts
├── changelog.ts
└── release.ts
└── tsconfig.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.jpg filter=lfs diff=lfs merge=lfs -text
2 | *.png filter=lfs diff=lfs merge=lfs -text
3 | *.webp filter=lfs diff=lfs merge=lfs -text
4 | *.avif filter=lfs diff=lfs merge=lfs -text
5 | *.jpeg filter=lfs diff=lfs merge=lfs -text
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve. Please check the Troubleshooting section before opening an issue.
4 | title: ''
5 | labels: 'bug: pending triage'
6 | assignees: ''
7 | ---
8 |
9 | [troubleshooting section]: https://iles-docs.netlify.app/faqs/troubleshooting
10 |
11 | - [ ] I have read the __[troubleshooting section]__ before opening an issue.
12 | - [ ] I have tried upgrading `iles` and `vite`.
13 |
14 | ### Description 📖
15 |
16 | _Provide a clear and concise description of what the bug is._
17 |
18 | ### Reproduction 🐞
19 |
20 | _Please provide a link to a repo that can reproduce the problem you ran into._
21 |
22 |
23 | Dependencies Info
24 |
25 | _Run `npx iles info` and `pnpm list` (or `npm list`) and provide the output:_
26 |
27 | ```
28 |
29 | ```
30 |
31 |
32 | ### Logs 📜
33 |
34 | _If not providing a reproduction:_
35 |
36 |
37 | Output
38 |
39 | _Run `DEBUG=iles:* npm run dev` or `DEBUG=iles:* npm run build` and provide the output:_
40 |
41 | ```
42 |
43 | ```
44 |
45 |
46 | ### Screenshots 📷
47 |
48 | _Provide console or browser screenshots of the problem_.
49 |
50 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Troubleshooting & FAQs
4 | url: https://iles-docs.netlify.app/faqs/troubleshooting
5 | about: 'Please check the most common configuration problems before opening an issue.'
6 | - name: Discord Chat
7 | url: https://discord.com/channels/804011606160703521/900707742203920394
8 | about: 'Discuss with other users in the #iles channel of the Vite.js Discord server.'
9 | - name: Questions & Discussions
10 | url: https://github.com/ElMassimo/iles/discussions
11 | about: Use GitHub discussions for questions and discussions.
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Description 📖
2 |
3 | This pull request
4 |
5 | ### Background 📜
6 |
7 | This was happening because
8 |
9 | ### The Fix 🔨
10 |
11 | By changing
12 |
13 | ### Screenshots 📷
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | .iles-ssg-temp/
4 | .vite-islands-temp/
5 | .DS_Store
6 | *.local
7 | *.tgz
8 | .pnpm-debug.log
9 | packages/.pnpm-debug.log
10 | components.d.ts
11 | composables.d.ts
12 |
13 | packages/iles/tests/renderAsync.ts
14 |
15 | .idea/
16 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | ignore-workspace-root-check=true
3 | strict-peer-dependencies=false
4 | auto-install-peers=true
5 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "Vue.volar",
4 | "dbaeumer.vscode-eslint",
5 | "esbenp.prettier-vscode",
6 | "antfu.unocss"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib",
3 | "files.exclude": {
4 | "**/dist": true
5 | },
6 | "files.watcherExclude": {
7 | "**/dist/**": true
8 | },
9 | "search.exclude": {
10 | "**/dist": true
11 | }
12 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Máximo Mussini
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Procfile.dev:
--------------------------------------------------------------------------------
1 | packages: npm run dev:packages
2 |
3 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .vitepress/metadata.json
4 | cypress/videos
5 | cypress/screenshots
6 |
7 | # Algolia
8 | .algolia.env
9 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | îles — french word for "islands"
9 |
10 |
11 | Islands of interactivity with Vue in Vite.js
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | [docs]: https://iles-docs.netlify.app
25 | [îles]: https://iles-docs.netlify.app
26 |
27 | # Documentation 📖
28 |
29 | This is the source code of the [documentation website for îles][docs].
30 |
31 | It's built using [îles].
32 |
33 | [__Live Website__][docs]
34 |
--------------------------------------------------------------------------------
/docs/composables.d.ts:
--------------------------------------------------------------------------------
1 | // generated by iles
2 | // We suggest you to commit this file into source control
3 |
4 | declare global {
5 | const definePageComponent: typeof import('iles')['definePageComponent']
6 | const useDocuments: typeof import('iles')['useDocuments']
7 | const useHead: typeof import('iles')['useHead']
8 | const usePage: typeof import('iles')['usePage']
9 | const useRoute: typeof import('iles')['useRoute']
10 | }
11 |
12 | export { }
13 |
--------------------------------------------------------------------------------
/docs/cypress.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | viewportWidth: 1280,
3 | chromeWebSecurity: false,
4 | retries: {
5 | runMode: 4,
6 | },
7 | e2e: {
8 | baseUrl: 'http://localhost:3050',
9 | },
10 | }
11 |
--------------------------------------------------------------------------------
/docs/cypress/e2e/dark-mode.cypress.cy.js:
--------------------------------------------------------------------------------
1 | import { visitHome, navigateTo, goBackHome, assertPage } from './helpers'
2 |
3 | describe('Dark Mode', () => {
4 | const toggleTheme = () => {
5 | cy.get(`[aria-label="Toggle theme"]`).click()
6 | }
7 |
8 | const assertTheme = (theme) =>
9 | cy.get('html').then((html) =>
10 | expect(html.hasClass('dark')).to.equal(theme === 'dark'))
11 |
12 | it('can toggle on and off', () => {
13 | visitHome()
14 | cy.get('html').then((html) => {
15 | if (html.hasClass('dark')) toggleTheme()
16 | assertTheme('light')
17 | })
18 |
19 | toggleTheme()
20 | assertTheme('dark')
21 |
22 | // Ensure Turbo hydrates after navigation.
23 | navigateTo('Install')
24 | assertPage({ title: 'Getting Started' })
25 | assertTheme('dark')
26 |
27 | toggleTheme()
28 | assertTheme('light')
29 |
30 | goBackHome()
31 |
32 | toggleTheme()
33 | assertTheme('dark')
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/docs/cypress/e2e/docsearch.cypress.cy.js:
--------------------------------------------------------------------------------
1 | import { visitHome, navigateTo, goBackHome, assertPage } from './helpers'
2 |
3 | describe('DocSearch', () => {
4 | const openSearchModal = () => {
5 | cy.get(`.nav-bar-button[aria-label="Search"]`).click()
6 | searchModal().should('be.visible')
7 | }
8 |
9 | const openSearchModalWithKeyboard = () => {
10 | cy.wait(100) // Give Turbo time to replace the body.
11 | cy.get('body').type('{cmd+k}')
12 | searchModal().should('be.visible')
13 | }
14 |
15 | const searchModal = () =>
16 | cy.get('.DocSearch-Modal')
17 |
18 | const closeSearchModal = () => {
19 | cy.get('body').type('{esc}')
20 | searchModal().should('not.exist')
21 | }
22 |
23 | it('can open by clicking or with keystroke', () => {
24 | visitHome()
25 | openSearchModal()
26 | closeSearchModal()
27 |
28 | openSearchModalWithKeyboard()
29 | closeSearchModal()
30 |
31 | // Ensure Turbo reactivates the islands.
32 | navigateTo('FAQs')
33 | assertPage({ title: 'FAQs' })
34 |
35 | openSearchModal()
36 | closeSearchModal()
37 |
38 | goBackHome()
39 | openSearchModalWithKeyboard()
40 | closeSearchModal()
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/docs/cypress/e2e/helpers.js:
--------------------------------------------------------------------------------
1 | export const assertPage = ({ title, content }) => {
2 | cy.get('h1').should('contain', title)
3 | if (content) cy.get('p').should('contain', content)
4 | }
5 |
6 | export const navigateTo = (title) => {
7 | cy.get('a').contains(title).click()
8 | waitForHydration()
9 | }
10 |
11 | export const visit = (path) => {
12 | cy.visit(path)
13 | waitForHydration()
14 | }
15 |
16 | export const visitHome = () =>
17 | visit('/')
18 |
19 | export const goBackHome = () => {
20 | cy.go('back')
21 | assertPage({ title: 'îles' })
22 | waitForHydration()
23 | }
24 |
25 | // Wait until the relevant islands are hydrated.
26 | export const waitForHydration = () => {
27 | cy.wait(100)
28 | cy.get('#ile-1[hydrated]').should('have.length', 1)
29 | cy.get('#ile-2[hydrated]').should('have.length', 1)
30 | cy.get('#ile-3[hydrated]').should('have.length', 1)
31 | }
32 |
--------------------------------------------------------------------------------
/docs/cypress/e2e/home.cypress.cy.js:
--------------------------------------------------------------------------------
1 | import { visitHome, navigateTo, assertPage, goBackHome } from './helpers'
2 |
3 | describe('The Home Page', () => {
4 | it('successfully loads', () => {
5 | visitHome()
6 | assertPage({ title: 'îles', content: 'The Joyful Site Generator' })
7 | cy.get('section')
8 | .should('contain', 'Partial Hydration')
9 | .should('contain', 'Ship JS only for the interactive bits')
10 |
11 | navigateTo('Get Started')
12 | assertPage({ title: 'Introduction' })
13 | cy.get('aside').should('contain', '🧱 Layouts and Components')
14 | cy.get('blockquote').should('contain', 'Project Status: Beta')
15 |
16 | goBackHome()
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/docs/cypress/e2e/sidebar.cypress.cy.js:
--------------------------------------------------------------------------------
1 | import { visit, navigateTo, goBackHome, assertPage, waitForHydration } from './helpers'
2 |
3 | describe('Sidebar Toggle', () => {
4 | const sidebar = () =>
5 | cy.get('#sidebar-panel')
6 |
7 | const sidebarToggle = () =>
8 | cy.get(`button[aria-label="Toggle Sidebar"]`)
9 |
10 | const openSidebar = () => {
11 | sidebarToggle().click()
12 | sidebar().should('be.visible')
13 | }
14 |
15 | const closeSidebar = () => {
16 | cy.get(`button[aria-label="Close Sidebar"]`).click()
17 | sidebar().should('not.be.visible')
18 | }
19 |
20 | it('is always open in mobile', () => {
21 | visitHome()
22 | visit('/guide/markdown')
23 | sidebar().should('be.visible')
24 | sidebarToggle().should('not.be.visible')
25 | })
26 |
27 | test.skipIf(process.env.CI)('can open in mobile', () => {
28 | visitHome()
29 | cy.viewport(500, 720)
30 | visit('/guide/frameworks')
31 | openSidebar()
32 | closeSidebar()
33 |
34 | // Ensure Turbo reactivates the islands.
35 | openSidebar()
36 | sidebar().contains('Config').click()
37 |
38 | // Give Turbo time to replace the body.
39 | waitForHydration()
40 | assertPage({ title: 'Config' })
41 | sidebar().should('not.be.visible')
42 |
43 | openSidebar()
44 | closeSidebar()
45 | })
46 | })
47 |
--------------------------------------------------------------------------------
/docs/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/docs/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add('login', (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/docs/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/docs/icons/arrowRight.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/icons/astro.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/icons/cloudflare.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/icons/vercel.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/icons/vite.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/icons/vue.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/docs/images/banner.avif:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:c55b939c3b59ea409fd3b51b175d71d78563f6dafd4dc00b2951a66dfbaa72b2
3 | size 58364
4 |
--------------------------------------------------------------------------------
/docs/images/banner.jpg:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:6fb44379216715de7b4d6386877bd889cbb5ee454d03a539ccdcb85b0c132f97
3 | size 73505
4 |
--------------------------------------------------------------------------------
/docs/images/banner.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:136b767e600483a9ede8bfb89055d057aaa4f7c2bba8bde74b14ba5fb7d1946e
3 | size 845704
4 |
--------------------------------------------------------------------------------
/docs/images/devtools-frameworks.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:84fed464d406afd2f56dc36d4c59138d76dca403b2752b0da108b603fc63ac57
3 | size 14296
4 |
--------------------------------------------------------------------------------
/docs/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ElMassimo/iles/c9e95d729013a8f8f425e894cc7785b954cbcd85/docs/images/favicon.ico
--------------------------------------------------------------------------------
/docs/images/logo-round.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:dae764870f9e01a99eb623f9efdf7c1a4463e8a1575a9323ac5f597be08cce0d
3 | size 37325
4 |
--------------------------------------------------------------------------------
/docs/images/logo.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:dc875bb06665f2bd26d28fcd2775dcd053cbae198263e73c9d51eab893f63780
3 | size 31168
4 |
--------------------------------------------------------------------------------
/docs/images/soon.jpg:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:bb2f7d5efaa2de07130d58aa183d35700b48214ecaf91c246ba37df1295602d6
3 | size 35123
4 |
--------------------------------------------------------------------------------
/docs/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/docs/modules/lastUpdated.ts:
--------------------------------------------------------------------------------
1 | import { sync as spawn } from 'cross-spawn'
2 | import type { IlesModule } from 'iles'
3 |
4 | export default () => ({
5 | name: 'git-last-updated-at',
6 | extendFrontmatter (frontmatter, filename) {
7 | const lastUpdated = lastUpdatedFromGit(filename)
8 | if (lastUpdated)
9 | frontmatter.meta.lastUpdated = lastUpdated
10 | },
11 | }) as IlesModule
12 |
13 | function lastUpdatedFromGit (filename: string) {
14 | try {
15 | const result = spawn('git', ['log', '-1', '--format=%at', filename])
16 | const date = new Date(parseInt(result.stdout as any) * 1000)
17 | return isNaN(Number(date)) ? null : date
18 | }
19 | catch {
20 | return null
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "1.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "dev": "iles --open --port 3001",
8 | "build": "iles build",
9 | "preview": "iles preview --open --port 3050",
10 | "now": "npm run build && npm run preview",
11 | "lint": "eslint .",
12 | "lint:fix": "eslint . --fix",
13 | "check": "vue-tsc --noEmit",
14 | "cy:run": "scripts/test-cypress"
15 | },
16 | "engines": {
17 | "node": "^14.18 || >= 16.0.0"
18 | },
19 | "devDependencies": {
20 | "@iconify-json/bx": "^1.1.10",
21 | "@iconify-json/heroicons-outline": "^1.1.10",
22 | "@islands/headings": "workspace:*",
23 | "@islands/icons": "workspace:*",
24 | "@islands/prism": "workspace:*",
25 | "@islands/pwa": "workspace:*",
26 | "@preact/preset-vite": "^2.9.0",
27 | "@types/cross-spawn": "^6.0.6",
28 | "@vueuse/core": "^10.11.0",
29 | "@vue-macros/reactivity-transform": "^1.0.4",
30 | "autoprefixer": "^10.4.19",
31 | "cross-spawn": "^7.0.3",
32 | "iles": "workspace:*",
33 | "postcss-nesting": "^12.1.5",
34 | "preact": "^10.24.3",
35 | "preact-render-to-string": "^6.5.11",
36 | "rehype-external-links": "^3.0.0",
37 | "unocss": "^0.61.8",
38 | "vite-plugin-inspect": "^0.8.7",
39 | "vue-tsc": "^2.1.10"
40 | },
41 | "dependencies": {
42 | "@docsearch/css": "^3.6.1",
43 | "@mussi/docsearch": "3.1.2-beta.0"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/docs/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | autoprefixer: {},
4 | 'postcss-nesting': {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/docs/public/_headers:
--------------------------------------------------------------------------------
1 | /
2 | x-frame-options: DENY
3 | x-xss-protection: 1; mode=block
4 |
5 | /faqs/*
6 | x-frame-options: DENY
7 | x-xss-protection: 1; mode=block
8 |
9 | /guide/*
10 | x-frame-options: DENY
11 | x-xss-protection: 1; mode=block
12 |
13 | /*.html
14 | x-frame-options: DENY
15 | x-xss-protection: 1; mode=block
16 |
17 | /assets/*
18 | cache-control: max-age=31536000
19 | cache-control: immutable
20 |
21 | /logo.svg
22 | cache-control: max-age=31536000
23 | cache-control: immutable
24 |
25 | /favicon.ico
26 | cache-control: max-age=31536000
27 | cache-control: immutable
28 |
29 | /*
30 | x-content-type-options: nosniff
31 | referrer-policy: no-referrer
32 | strict-transport-security: max-age=31536000; includeSubDomains
33 | content-security-policy-report-only: default-src 'self'; font-src 'self'; img-src 'self' data: developer.stackblitz.com; script-src 'self' 'unsafe-inline'; connect-src 'self' 'unsafe-inline'; style-src 'self'
34 |
--------------------------------------------------------------------------------
/docs/public/_redirects:
--------------------------------------------------------------------------------
1 | /config/plugins https://iles-docs.netlify.app/guide/plugins 301
2 | /guide/comparisons https://iles-docs.netlify.app/faqs 301
3 |
--------------------------------------------------------------------------------
/docs/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:3aabe05feb65df10e43dd18353401ae2fc395823bd029f95a1c8ed806b45220f
3 | size 5099
4 |
--------------------------------------------------------------------------------
/docs/public/pwa-192x192.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:a68ec9fbc02330633e4b62812ad01f9f588e4d08d6f66ea68b830efb646b47e1
3 | size 5455
4 |
--------------------------------------------------------------------------------
/docs/public/pwa-512x512.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:cca23b5c3541651b928085f740977ce7c16633a7fdd62e72ceb23c0e90b4cbfb
3 | size 13203
4 |
--------------------------------------------------------------------------------
/docs/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/docs/scripts/test-cypress:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # scripts/test-cypress: Starts the local server and runs Cypress tests.
4 | set -e
5 | cd "$(dirname "$0")/.."
6 |
7 | echo '== Starting server =='
8 | pnpm run preview &
9 |
10 | echo '== Waiting for server to respond... =='
11 | pnpx wait-on http-get://localhost:3050 --delay 1000 --timeout 15000
12 |
13 | echo '== Server ready! =='
14 |
15 | pnpx cypress@10.6.0 run && echo '== Finished! =='
16 |
--------------------------------------------------------------------------------
/docs/src/app.ts:
--------------------------------------------------------------------------------
1 | import 'virtual:uno.css'
2 | import '@unocss/reset/tailwind-compat.css'
3 | import '~/styles/all.css'
4 |
5 | import { defineApp } from 'iles'
6 |
7 | import Image from '~/components/Image.vue'
8 |
9 | import checkDarkTheme from '~/logic/dark-color-scheme-check?raw'
10 | import type { Script } from '@unhead/schema'
11 |
12 | type TurboScript = Script & { once: true }
13 |
14 | export default defineApp({
15 | head: {
16 | htmlAttrs: { lang: 'en-US' },
17 | script: [
18 | { children: checkDarkTheme, once: true } as TurboScript,
19 | ],
20 | },
21 | mdxComponents: {
22 | img: Image,
23 | },
24 | })
25 |
--------------------------------------------------------------------------------
/docs/src/components/AppButton.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
79 |
--------------------------------------------------------------------------------
/docs/src/components/AstroLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/AutoImported.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | You don't need to import usePage , useRoute , useHead , or definePageComponent .
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/Caption.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/docs/src/components/CloudflareLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/DarkModeSwitch.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
33 |
--------------------------------------------------------------------------------
/docs/src/components/DocSearch.tsx:
--------------------------------------------------------------------------------
1 | import { DocSearch, DocSearchProps } from '@mussi/docsearch'
2 | import '~/styles/docsearch.css'
3 |
4 | const options: Partial = {
5 | transformItems (items) {
6 | return items.map((item) => {
7 | const getRelativePath = (url: string) => {
8 | const { pathname, hash } = new URL(url)
9 | return pathname + hash
10 | }
11 | return Object.assign({}, item, { url: getRelativePath(item.url) })
12 | })
13 | },
14 | }
15 |
16 | const IlesDocSearch = (props: Partial) =>
17 |
24 |
25 | export default IlesDocSearch
26 |
--------------------------------------------------------------------------------
/docs/src/components/EditLink.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
14 | Suggest changes to this page
15 |
16 |
17 |
--------------------------------------------------------------------------------
/docs/src/components/GitHubLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/HomeFeatures.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
18 |
--------------------------------------------------------------------------------
/docs/src/components/HomeHero.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{ $site.title }}
22 |
23 |
24 | {{ $frontmatter.tagline || $site.description }}
25 |
26 |
27 |
28 |
29 | {{ $frontmatter.actionText }}
30 |
31 |
32 |
33 | {{ $frontmatter.altActionText }}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
55 |
--------------------------------------------------------------------------------
/docs/src/components/Iles.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/src/components/Image.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 | {{ alt }}
13 |
14 |
--------------------------------------------------------------------------------
/docs/src/components/LastUpdated.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Last Updated:
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/src/components/Logo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/MainContainer.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
37 |
--------------------------------------------------------------------------------
/docs/src/components/MetaTags.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/docs/src/components/NavBarButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
16 |
--------------------------------------------------------------------------------
/docs/src/components/NavBarLink.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
21 | {{ text }}
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/src/components/NavBarLinks.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/src/components/NavBarLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/src/components/NetlifyLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/NextAndPrevLinks.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
24 |
25 |
26 |
35 |
--------------------------------------------------------------------------------
/docs/src/components/OutboundLink.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
16 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/docs/src/components/PageFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/src/components/PageHooks.mdx:
--------------------------------------------------------------------------------
1 | [extendFrontmatter]: /guide/plugins#islandspages
2 | [extendRoute]: /guide/plugins#islandspages
3 |
4 |
5 | You can customize all pages using the [extendFrontmatter] and [extendRoute] hooks.
6 |
7 |
--------------------------------------------------------------------------------
/docs/src/components/ReloadPrompt.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
28 |
New content is available
29 |
Close
30 |
Reload
31 |
32 |
33 |
34 |
47 |
--------------------------------------------------------------------------------
/docs/src/components/SearchButton.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/src/components/SidebarBackground.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/src/components/SidebarHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/src/components/SidebarLink.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/docs/src/components/SidebarLinkItem.vue:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 | {{ item.text }}
28 |
29 |
30 | {{ item.text }}
31 |
32 |
33 |
34 |
68 |
--------------------------------------------------------------------------------
/docs/src/components/SidebarNav.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/docs/src/components/SidebarToggle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/TableOfContents.vue:
--------------------------------------------------------------------------------
1 |
38 |
39 |
40 |
48 |
49 |
--------------------------------------------------------------------------------
/docs/src/components/TheFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Released under the MIT License.
4 | Copyright © 2021–{{ $site.year }} {{ $site.author }}
5 |
6 |
7 | Built with
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
--------------------------------------------------------------------------------
/docs/src/components/TheNavBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
--------------------------------------------------------------------------------
/docs/src/components/TheRightSidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/src/components/TimeAgo.vue:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 | {{ relativeTimeStr || dateStr }}
33 |
34 |
--------------------------------------------------------------------------------
/docs/src/components/Tip.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 | {{ title }}
12 |
13 |
14 |
15 |
16 |
17 |
27 |
--------------------------------------------------------------------------------
/docs/src/components/VercelLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/ViteLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/VueLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/src/components/contact.mdx:
--------------------------------------------------------------------------------
1 | [twitter]: https://twitter.com/ilesjs
2 | [follow me]: https://twitter.com/MaximoMussini
3 | [GitHub Issues]: https://github.com/ElMassimo/iles/issues
4 | [GitHub Discussions]: https://github.com/ElMassimo/iles/discussions
5 | [project]: https://github.com/ElMassimo/iles
6 |
7 | ## Contact ✉️
8 |
9 | Please visit [GitHub Issues] to report bugs you find, and [GitHub Discussions] to make feature requests, or to get help.
10 |
11 | Show some love by [⭐️ starring the project][project] if you find it useful!
12 |
13 | ## News 🗞
14 |
15 | [Follow me] or the [official îles account][twitter] on [Twitter].
16 |
--------------------------------------------------------------------------------
/docs/src/components/should.mdx:
--------------------------------------------------------------------------------
1 | [Vuepress]: https://v2.vuepress.vuejs.org/
2 | [Jekyll]: https://github.com/ElMassimo/jekyll-vite
3 | [eleventy]: https://www.11ty.dev/
4 |
5 | ## Should I use ?
6 |
7 | There are more mature tools that might be a better choice, such as [Vuepress].
8 |
9 | The goal of this project is to combine the ease of use and development experience of
10 | building a site with Vue, while effortlessly shipping a zero JS site as if you
11 | were using [Jekyll] or [eleventy].
12 |
13 |
14 | APIs may change on minor releases. Lock the version to avoid breakage.
15 |
16 |
--------------------------------------------------------------------------------
/docs/src/layouts/base.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/src/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/src/layouts/home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/docs/src/logic/config.ts:
--------------------------------------------------------------------------------
1 | export interface SiteConfig {
2 | nav?: NavItem[] | false
3 | sidebar?: SideBarItem[]
4 | }
5 |
6 | // navbar --------------------------------------------------------------------
7 |
8 | export type NavItem = NavItemWithLink | NavItemWithChildren
9 |
10 | export interface NavItemBase {
11 | text: string
12 | target?: string
13 | rel?: string
14 | ariaLabel?: string
15 | activeMatch?: string
16 | }
17 |
18 | export interface NavItemWithLink extends NavItemBase {
19 | link: string
20 | }
21 |
22 | export interface NavItemWithChildren extends NavItemBase {
23 | items: NavItemWithLink[]
24 | }
25 |
26 | // sidebar -------------------------------------------------------------------
27 |
28 | export type SideBarItem = SideBarLink | SideBarGroup
29 |
30 | export interface SideBarLink {
31 | text: string
32 | link: string
33 | }
34 |
35 | export interface SideBarGroup extends SideBarLink {
36 | children: SideBarItem[]
37 | }
38 |
--------------------------------------------------------------------------------
/docs/src/logic/dark-color-scheme-check.ts:
--------------------------------------------------------------------------------
1 | (() => {
2 | const prefersDark = matchMedia('(prefers-color-scheme: dark)').matches
3 | const setting = localStorage.getItem('vueuse-color-scheme') || 'auto'
4 | if (setting === 'dark' || (prefersDark && setting !== 'light'))
5 | document.documentElement.classList.toggle('dark', true)
6 | })()
7 |
--------------------------------------------------------------------------------
/docs/src/logic/dark.ts:
--------------------------------------------------------------------------------
1 | import { useDark, useToggle } from '@vueuse/core'
2 |
3 | export const isDark = useDark()
4 | export const toggleDark = useToggle(isDark)
5 |
--------------------------------------------------------------------------------
/docs/src/logic/utils.ts:
--------------------------------------------------------------------------------
1 | const hashRE = /#.*$/
2 | const extRE = /(index)?\.(md|html)$/
3 | const endingSlashRE = /\/$/
4 |
5 | /**
6 | * Remove `.md` or `.html` extention from the given path. It also converts
7 | * `index` to slush.
8 | */
9 | export function normalize (path: string): string {
10 | return ensureStartingSlash(decodeURI(path).replace(hashRE, '').replace(extRE, '').replace(endingSlashRE, ''))
11 | }
12 |
13 | function ensureStartingSlash (path: string): string {
14 | return path.startsWith('/') ? path : `/${path}`
15 | }
16 |
17 | export function joinUrl (base: string, path: string): string {
18 | if (path.startsWith('#')) return path
19 | return `${base}${path.startsWith('/') ? path.slice(1) : path}`
20 | }
21 |
--------------------------------------------------------------------------------
/docs/src/pages/404.vue:
--------------------------------------------------------------------------------
1 |
2 | title: Not Found
3 |
4 |
5 |
6 |
7 |
8 |
9 |
404
10 |
If you think there should be something here, please contact me .
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/src/pages/dynamic/[section].vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
18 |
19 |
20 | Dynamic Path Example
21 |
22 | Back to Routing .
23 |
24 |
--------------------------------------------------------------------------------
/docs/src/pages/faqs/troubleshooting.mdx:
--------------------------------------------------------------------------------
1 | [GitHub Issues]: https://github.com/ElMassimo/iles/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
2 | [GitHub Discussions]: https://github.com/ElMassimo/iles/discussions
3 | [layout]: /guide/development#layouts
4 | [deployment]: /guide/deployment
5 |
6 | # Troubleshooting
7 |
8 | This section lists a few common gotchas, and bugs introduced in the past.
9 |
10 | Please skim through __before__ opening an [issue][GitHub Issues].
11 |
12 | ## Page data not properly injected in app
13 |
14 | `usePage` can not be used inside islands, as it would cause the entire context
15 | (page, frontmatter, meta) to be included in the bundle.
16 |
17 | Instead, pass the necessary data explicitly as props. For example:
18 |
19 | ```vue
20 |
21 | ```
22 |
23 | ## Could not load layout file during build
24 |
25 | [Layout file names must be lowercase][layout]: `default.vue` instead of `Default.vue`.
26 |
27 | Most [service providers][deployment] are Linux-based, which is case-sensitive.
28 | This is why it might work in your local macOS or Windows, but fail when [deploying][deployment].
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/src/pages/guide/index.mdx:
--------------------------------------------------------------------------------
1 | [introduction]: /guide/introduction
2 | [configuration reference]: /config
3 | [starter]: https://github.com/ElMassimo/create-iles
4 |
5 | # Getting Started
6 |
7 | If you are interested in learning more about before trying it, read the [Introduction].
8 |
9 | If you are looking for configuration options, visit the [configuration reference].
10 |
11 | ## Try it Online ⚡️
12 |
13 | import stackblitzSrc from '/images/stackblitz.svg'
14 |
15 |
16 |
17 |
18 |
19 | ## Installation 💿
20 |
21 | Run the following command to create a new îles project.
22 |
23 | ```bash
24 | pnpm create iles@next # or npm or yarn
25 | ```
26 |
27 | The [starter] comes with Vue components and examples. Once you have installed
28 | all dependencies, give it a try by running npm run dev to start the
29 | development server.
30 |
31 | Alternatively, add `iles` to your `package.json` in an existing project.
32 |
33 | ## Usage 🚀
34 |
35 | The `iles` executable provides the following commands:
36 |
37 | - iles dev : Starts the development server
38 | - iles build : Creates a production build of the site
39 | - iles preview : Preview the site after building
40 |
41 | The following shortcuts are added by default when using the [starter]:
42 |
43 | ```json
44 | "scripts": {
45 | "dev": "iles dev --open",
46 | "build": "iles build",
47 | "preview": "iles preview --open"
48 | },
49 | ```
50 |
51 | - npm run dev : Starts the development server
52 | - npm run build : Creates a production build of the site
53 | - npm run preview : Preview the site after building
54 |
55 |
56 |
--------------------------------------------------------------------------------
/docs/src/pages/guide/overview.mdx:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/src/pages/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 |
4 | actionText: Get Started
5 | actionLink: /guide/introduction
6 |
7 | altActionText: Install
8 | altActionLink: /guide
9 |
10 | heroAlt: îles logo
11 |
12 | features:
13 | - title: 🏝 Partial Hydration
14 | details: Ship JS only for the interactive bits, by default that's zero.
15 | href: /guide/hydration
16 | - title: 🧱 Multi-Framework
17 | details: Build islands with Vue, Preact, SolidJS, Svelte, or plain JS.
18 | href: /guide/frameworks
19 | - title: 📖 Markdown Support
20 | details: Use components inside Markdown, with auto-import.
21 | href: /guide/markdown
22 | - title: ⚡ Batteries Included
23 | details: Layouts, routing, frontmatter for pages, plugins, and more.
24 | href: /guide/development
25 | ---
26 |
--------------------------------------------------------------------------------
/docs/src/styles/all.css:
--------------------------------------------------------------------------------
1 | @import "~/styles/variables";
2 | @import "~/styles/utilities";
3 | @import "~/styles/global";
4 | @import "~/styles/navbar";
5 | @import "~/styles/themes";
6 | @import "~/styles/highlight";
7 | @import "~/styles/prose";
8 |
--------------------------------------------------------------------------------
/docs/src/styles/navbar.css:
--------------------------------------------------------------------------------
1 | #navbar-actions {
2 | height: 100%;
3 | width: 80px;
4 | }
5 |
6 | .navbar {
7 | /* https://unocss.dev/presets/wind#bracket-syntax-spaces */
8 | @apply p-4 gap-4 grid-cols-[min-content_1fr_min-content]
9 | sm:(py-8 text-lg)
10 | lg:py-12;
11 |
12 | max-width: calc(6rem + var(--w-container) + (100vw - var(--w-container)) / 2);
13 | }
14 |
15 | .navbar-menu {
16 | @apply h-8 leading-5 place-items-center ml-auto;
17 |
18 | & > li + li,
19 | & + div,
20 | & + ile-root > div {
21 | @apply ml-4 lg:ml-8;
22 | }
23 | }
24 |
25 | .navbar-menu-item {
26 | @apply -mt-0.5;
27 |
28 | & > a {
29 | @apply opacity-80 pt-1 border-transparent border-bottom-2;
30 |
31 | transition: opacity 80ms ease, border-color 80ms ease;
32 |
33 | &:hover,
34 | &:active,
35 | &.active {
36 | @apply border-primary opacity-100;
37 | }
38 | }
39 | }
40 |
41 | .navbar-actions {
42 | @apply gap-2;
43 | }
44 |
--------------------------------------------------------------------------------
/docs/src/styles/themes.css:
--------------------------------------------------------------------------------
1 | :root {
2 | /* Colors */
3 | --h-background: #fbfbfb;
4 | --h-foreground: #393a34;
5 |
6 | /* Tokens */
7 | --h-boolean: #1c6b48;
8 | --h-builtin: #ab5959;
9 | --h-class: #795da3;
10 | --h-comment: #a0ada0;
11 | --h-constant: #2993a3;
12 | --h-decorator: #bd8f8f;
13 | --h-deleted: #a14f55;
14 | --h-function: #795da3;
15 | --h-keyword: #a71d5d;
16 | --h-literal: #2f8a89;
17 | --h-namespace: #b05a78;
18 | --h-number: #296aa3;
19 | --h-property: #b58451;
20 | --h-punctuation: #a71d5d;
21 | --h-regex: #ab5e3f;
22 | --h-string: #b56959;
23 | --h-operator: var(--h-punctuation);
24 | --h-variable: var(--h-literal);
25 | --h-symbol: var(--h-literal);
26 | --h-interpolation: var(--h-literal);
27 | --h-selector: var(--h-keyword);
28 | --h-keyword-control: var(--h-keyword);
29 | }
30 |
31 | html.dark {
32 | --h-foreground: #d4cfbf;
33 | --h-background: #1e1e1e;
34 | --h-boolean: #1c6b48;
35 | --h-builtin: #e0a569;
36 | --h-class: #54b1bf;
37 | --h-comment: #758575;
38 | --h-constant: var(--h-literal);
39 | --h-decorator: #bd8f8f;
40 | --h-deleted: #a14f55;
41 | --h-function: #67b1b5;
42 | --h-keyword: #e3428c;
43 | --h-literal: #429988;
44 | --h-namespace: #db889a;
45 | --h-number: #6394bf;
46 | --h-property: #dd8e6e;
47 | --h-punctuation: #858585;
48 | --h-regex: #ab5e3f;
49 | --h-string: #d48372;
50 | --h-variable: #c2b36e;
51 | }
52 |
--------------------------------------------------------------------------------
/docs/src/styles/utilities.css:
--------------------------------------------------------------------------------
1 | .website-logo {
2 | @apply transition-transform duration-400 hover:scale-110;
3 | }
4 |
5 | .section-title {
6 | @apply text-3xl sm:text-4xl md:text-5xl leading-tight font-extrabold text-hard my-8;
7 | }
8 |
9 | .layout {
10 | @apply mx-auto px-8 my-8;
11 |
12 | max-width: calc(var(--w-container) + 15vw);
13 | }
14 |
15 | .container {
16 | margin: 0 auto;
17 | max-width: var(--w-container);
18 | }
19 |
20 | .hoverable {
21 | transition: background 80ms ease;
22 |
23 | &.active,
24 | &:hover {
25 | background: var(--bg-highlight);
26 | }
27 | }
28 |
29 | .transition-bg {
30 | transition: background 0.3s ease;
31 | }
32 |
--------------------------------------------------------------------------------
/docs/src/styles/variables.css:
--------------------------------------------------------------------------------
1 | :root {
2 | /* https://github.com/unocss/unocss/blob/main/packages/reset/tailwind.md */
3 | --un-default-border-color: var(--br-normal);
4 | --navbar-height: 3.5rem;
5 | --footer-height: 172px;
6 | --full-viewport: calc(100vh - var(--navbar-height));
7 | --br-normal: rgba(126, 135, 160, 0.15);
8 | --bg-html: theme('colors.white');
9 | --bg-scrollbar-track: var(--bg-highlight);
10 | --bg-scrollbar-thumb: var(--bg-soft);
11 | --bg-scrollbar-thumb-hover: var(--bg-primary);
12 | --bg-highlight: rgba(126, 185, 220, 0.15);
13 | --bg-primary: var(--fc-primary-soft);
14 | --bg-soft: rgba(126, 185, 220, 0.35);
15 | --bg-subtle: theme('colors.gray.50');
16 | --fc-softer: theme('colors.gray.400');
17 | --fc-soft: theme('colors.gray.500');
18 | --fc: theme('colors.gray.700');
19 | --fc-hard: theme('colors.gray.800');
20 | --fc-intense: theme('colors.gray.900');
21 | --fc-primary-soft: theme('colors.lightblue.500');
22 | --fc-primary: theme('colors.lightblue.600');
23 | --fc-primary-intense: theme('colors.lightblue.700');
24 | --w-container: 1376px;
25 | }
26 |
27 | @screen md {
28 | :root {
29 | --navbar-height: 3.75rem;
30 | }
31 | }
32 |
33 | html.dark {
34 | --bg-html: theme('colors.warmgray.900');
35 | --br-normal: rgba(226, 235, 255, 0.15);
36 | --bg-soft: rgba(195, 185, 185, 0.15);
37 | --fc-softer: theme('colors.warmgray.500');
38 | --fc-soft: theme('colors.warmgray.500');
39 | --fc: theme('colors.warmgray.300');
40 | --fc-hard: theme('colors.warmgray.200');
41 | --fc-intense: theme('colors.warmgray.100');
42 | --fc-primary-soft: theme('colors.lightblue.500');
43 | --fc-primary-intense: theme('colors.lightblue.500');
44 |
45 | color-scheme: dark
46 | }
47 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "module": "esnext",
5 | "target": "esnext",
6 | "moduleResolution": "node",
7 | "allowJs": true,
8 | "jsx": "preserve",
9 | "jsxFactory": "h",
10 | "jsxFragmentFactory": "Fragment",
11 | "strict": true,
12 | "declaration": true,
13 | "noUnusedLocals": true,
14 | "skipLibCheck": true,
15 | "esModuleInterop": true,
16 | "lib": ["esnext", "DOM"],
17 | "types": [
18 | "@vue-macros/reactivity-transform/macros-global",
19 | "vite-plugin-pwa/client"
20 | ],
21 | "paths": {
22 | "~/*": ["src/*"]
23 | }
24 | },
25 | "exclude": [
26 | "node_modules/cypress"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/docs/uno.config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | defineConfig,
3 | presetIcons,
4 | // presetTypography,
5 | presetUno,
6 | transformerDirectives,
7 | } from 'unocss'
8 | import transformerVariantGroup from '@unocss/transformer-variant-group'
9 |
10 | export default defineConfig({
11 | transformers: [transformerDirectives(), transformerVariantGroup()],
12 | presets: [
13 | presetUno(),
14 | // presetTypography(),
15 | presetIcons({
16 | prefix: 'i-', // default prefix
17 | }),
18 |
19 | ],
20 | safelist: ['blockquote', 'figure', 'code'],
21 | blocklist: ['content-type', 'content', 'container', 'table', '",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/ElMassimo/iles"
28 | },
29 | "homepage": "https://github.com/ElMassimo/iles",
30 | "bugs": "https://github.com/ElMassimo/iles/issues",
31 | "dependencies": {
32 | "estree-walker": "^3.0",
33 | "hast-util-to-string": "^3.0.0"
34 | },
35 | "devDependencies": {
36 | "@types/estree": "^1.0.6",
37 | "@types/estree-jsx": "^1.0.5",
38 | "@types/hast": "^3.0.4",
39 | "iles": "workspace:*",
40 | "tsup": "8.2.4",
41 | "typescript": "^5.6.3",
42 | "unified": "^11.0.5"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/excerpt/src/excerpt.cjs:
--------------------------------------------------------------------------------
1 | module.exports = (...args) => new Promise((resolve, reject) => {
2 | import('../dist/excerpt.js')
3 | .then(m => resolve(m.default(...args)))
4 | .catch(reject)
5 | })
6 |
--------------------------------------------------------------------------------
/packages/excerpt/src/excerpt.ts:
--------------------------------------------------------------------------------
1 | import type { IlesModule } from 'iles'
2 | import type { ExcerptOptions, Options, SeparatorFn } from './types'
3 | import type { Element, Comment } from 'hast'
4 | import { recmaPlugin } from './recma-plugin'
5 | import { rehypePlugin } from './rehype-plugin'
6 |
7 | declare module 'iles' {
8 | interface PageMeta {
9 | /**
10 | * Excerpt for MDX documents.
11 | */
12 | excerpt?: string
13 | }
14 | }
15 |
16 | export * from './types'
17 |
18 | /**
19 | * An iles module that sets `meta.excerpt` for MDX documents.
20 | * Also enables an `excerpt: true` prop in MDX components to render HTML.
21 | */
22 | export default function IlesExcerpts (userOptions: ExcerptOptions = {}): IlesModule {
23 | const { separator = ['excerpt', 'Excerpt'], ...rest } = userOptions
24 | const options: Options = { ...rest, isSeparator: separatorFnFrom(separator) }
25 |
26 | return {
27 | name: '@islands/excerpt',
28 | markdown: {
29 | rehypePlugins: [
30 | [rehypePlugin, options],
31 | ],
32 | recmaPlugins: [
33 | recmaPlugin,
34 | ],
35 | },
36 | }
37 | }
38 |
39 | function separatorFnFrom (separator: string | string[] | SeparatorFn): SeparatorFn {
40 | if (isSeparatorFn(separator))
41 | return separator
42 |
43 | const separators = new Set(Array.isArray(separator) ? separator : [separator])
44 |
45 | return (node) => {
46 | if (node.type === 'element') return separators.has((node as Element).tagName)
47 | // @ts-ignore
48 | if (node.type === 'mdxJsxFlowElement') return separators.has(node.name)
49 | if (node.type === 'comment') return separators.has((node as Comment).value.trim())
50 | return false
51 | }
52 | }
53 |
54 | function isSeparatorFn (val: any): val is SeparatorFn {
55 | return typeof val === 'function'
56 | }
57 |
--------------------------------------------------------------------------------
/packages/excerpt/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { Node } from 'hast'
2 | import type { VFile } from 'vfile'
3 |
4 | export type ExtractFn = (content: string, vfile: VFile) => string | undefined
5 |
6 | export type SeparatorFn = (node: Node, index: number) => boolean
7 |
8 | export interface ExcerptOptions {
9 | /**
10 | * Function to extract the excerpt from an MDX document.
11 | */
12 | extract?: ExtractFn
13 | /**
14 | * Tag name(s) of the separator or a function that returns true when it finds one.
15 | * @default ['excerpt', 'Excerpt']
16 | */
17 | separator?: string | string[] | SeparatorFn
18 | /**
19 | * An optional max length for the extracted excerpt.
20 | */
21 | maxLength?: number
22 | }
23 |
24 | export interface Options {
25 | extract?: ExtractFn
26 | isSeparator: SeparatorFn
27 | maxLength?: number
28 | }
29 |
--------------------------------------------------------------------------------
/packages/excerpt/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | splitting: true,
7 | format: ['esm'],
8 | }
9 |
--------------------------------------------------------------------------------
/packages/feed/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
@islands/feed
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 | [routing]: https://iles-docs.netlify.app/guide/routing
23 | [feed]: https://github.com/jpmonette/feed
24 | [rss]: https://iles-docs.netlify.app/guide/rss
25 |
26 | An [îles] module to generate feeds for your site:
27 |
28 | - 📻 supports RSS, Atom, and JSON feeds
29 |
30 | - ⚡️ HMR during development to debug the result
31 |
32 | - 💪🏼 strongly typed, powered by [`feed`][feed]
33 |
34 | ### Installation 💿
35 |
36 | ```ts
37 | // iles.config.ts
38 | import { defineConfig } from 'iles'
39 |
40 | export default defineConfig({
41 | modules: [
42 | '@islands/feed',
43 | ],
44 | })
45 | ```
46 |
47 | See the [_RSS Feeds_ section of the docs][rss] for usage instructions.
48 |
--------------------------------------------------------------------------------
/packages/feed/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/feed",
3 | "version": "0.10.0-beta.1",
4 | "scripts": {
5 | "dev": "npm run build -- --watch",
6 | "build": "tsup src/types.ts src/feed.ts src/render-feed.ts",
7 | "lint": "eslint .",
8 | "lint:fix": "eslint . --fix"
9 | },
10 | "files": [
11 | "dist",
12 | "src"
13 | ],
14 | "types": "dist/feed.d.ts",
15 | "type": "module",
16 | "exports": {
17 | ".": {
18 | "import": "./dist/feed.js",
19 | "require": "./src/feed.cjs"
20 | },
21 | "./package.json": "./package.json"
22 | },
23 | "funding": "https://github.com/sponsors/ElMassimo",
24 | "author": "Máximo Mussini ",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/ElMassimo/iles"
28 | },
29 | "homepage": "https://github.com/ElMassimo/iles",
30 | "bugs": "https://github.com/ElMassimo/iles/issues",
31 | "devDependencies": {
32 | "iles": "workspace:*",
33 | "vue": "^3.5.12"
34 | },
35 | "peerDependencies": {
36 | "iles": "workspace:*",
37 | "vue": "^3.3.4"
38 | },
39 | "dependencies": {
40 | "feed": "^4.2",
41 | "pathe": "^1.1.2"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/feed/src/feed.cjs:
--------------------------------------------------------------------------------
1 | module.exports = (...args) => new Promise((resolve, reject) => {
2 | import('../dist/feed.js')
3 | .then(m => resolve(m.default(...args)))
4 | .catch(reject)
5 | })
6 |
--------------------------------------------------------------------------------
/packages/feed/src/feed.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath } from 'url'
2 | import { dirname, join } from 'pathe'
3 | import type { IlesModule } from 'iles'
4 |
5 | export * from './types'
6 |
7 | const __dirname = dirname(fileURLToPath(import.meta.url))
8 |
9 | /**
10 | * An iles module that provides a component to generate RSS, Atom, and JSON feeds.
11 | */
12 | export default function IlesFeed (): IlesModule {
13 | return {
14 | name: '@islands/feed',
15 | components: {
16 | resolvers: [
17 | (name) => {
18 | if (name === 'RenderFeed')
19 | return { name: 'RenderFeed', from: join(__dirname, 'render-feed') }
20 | },
21 | ],
22 | },
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/feed/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { FeedOptions, Item as ResolvedItem, Author, Extension } from 'feed'
2 | import type { VueRenderable } from 'iles'
3 |
4 | export type AsyncContent = string | VueRenderable
5 |
6 | export type { FeedOptions, ResolvedItem, Author, Extension }
7 |
8 | export interface FeedItem extends Omit {
9 | description?: AsyncContent
10 | content?: AsyncContent
11 | }
12 |
13 | export type FeedFormat = 'atom1' | 'rss2' | 'json1'
14 |
--------------------------------------------------------------------------------
/packages/feed/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | splitting: false,
7 | format: ['esm'],
8 | }
9 |
--------------------------------------------------------------------------------
/packages/headings/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
@islands/headings
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 | [docs]: https://iles-docs.netlify.app
23 | [rehype]: https://github.com/rehypejs/rehype
24 | [markdown]: https://iles-docs.netlify.app/guide/markdown
25 |
26 | An [îles] module that injects a [rehype] plugin to parse headings in
27 | [MDX documents][markdown]:
28 |
29 | - 🔗 adds an id to headings and injects an anchor tag to link them
30 |
31 | - 🏷 automatically extracts the title from an `` and sets `frontmatter.title`
32 |
33 | - 📖 sets `meta.headings` to enable rendering sidebars and table of contents
34 |
35 | ### Usage 🚀
36 |
37 | ```ts
38 | // iles.config.ts
39 | import { defineConfig } from 'iles'
40 |
41 | export default defineConfig({
42 | modules: [
43 | '@islands/headings',
44 | ],
45 | })
46 | ```
47 |
--------------------------------------------------------------------------------
/packages/headings/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/headings",
3 | "version": "0.10.0-beta.1",
4 | "scripts": {
5 | "dev": "npm run build -- --watch",
6 | "build": "tsup src/headings.ts",
7 | "lint": "eslint .",
8 | "lint:fix": "eslint . --fix"
9 | },
10 | "type": "module",
11 | "files": [
12 | "dist",
13 | "src"
14 | ],
15 | "types": "dist/headings.d.ts",
16 | "exports": {
17 | ".": {
18 | "import": "./dist/headings.js",
19 | "require": "./src/headings.cjs"
20 | },
21 | "./package.json": "./package.json"
22 | },
23 | "funding": "https://github.com/sponsors/ElMassimo",
24 | "author": "Máximo Mussini ",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/ElMassimo/iles"
28 | },
29 | "homepage": "https://github.com/ElMassimo/iles",
30 | "bugs": "https://github.com/ElMassimo/iles/issues",
31 | "dependencies": {
32 | "hast-util-heading-rank": "^3.0.0",
33 | "hast-util-to-string": "^3.0.0"
34 | },
35 | "devDependencies": {
36 | "@types/estree": "^1.0.5",
37 | "iles": "workspace:*",
38 | "slugo": "^0.4.0",
39 | "tsup": "8.2.4",
40 | "typescript": "^5.6.3",
41 | "unified": "^11.0.5"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/headings/src/headings.cjs:
--------------------------------------------------------------------------------
1 | module.exports = (...args) => new Promise((resolve, reject) => {
2 | import('../dist/headings.js')
3 | .then(m => resolve(m.default(...args)))
4 | .catch(reject)
5 | })
6 |
--------------------------------------------------------------------------------
/packages/headings/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | splitting: true,
7 | format: ['esm'],
8 | }
9 |
--------------------------------------------------------------------------------
/packages/hydration/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
@islands/hydration
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 |
23 | Internal utility for [îles] to hydrate Vue, Svelte, Preact, and Solid components.
24 |
--------------------------------------------------------------------------------
/packages/hydration/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/hydration",
3 | "description": "Hydration utilities for îles",
4 | "version": "0.10.0-beta.1",
5 | "scripts": {
6 | "dev": "npm run build -- --watch",
7 | "build": "tsup hydration.ts preact.ts vue.ts vanilla.ts solid.ts svelte.ts",
8 | "lint": "eslint .",
9 | "lint:fix": "eslint . --fix"
10 | },
11 | "type": "module",
12 | "files": [
13 | "dist",
14 | "island.svelte"
15 | ],
16 | "types": "dist/hydration.d.ts",
17 | "module": "dist/hydration.js",
18 | "exports": {
19 | ".": "./dist/hydration.js",
20 | "./preact": "./dist/preact.js",
21 | "./solid": "./dist/solid.js",
22 | "./svelte": "./dist/svelte.js",
23 | "./vanilla": "./dist/vanilla.js",
24 | "./vue": "./dist/vue.js",
25 | "./dist/*": "./dist/*",
26 | "./package.json": "./package.json"
27 | },
28 | "keywords": [
29 | "vite",
30 | "vue",
31 | "islands",
32 | "ssg"
33 | ],
34 | "author": "Máximo Mussini",
35 | "license": "MIT",
36 | "homepage": "https://github.com/ElMassimo/iles",
37 | "bugs": {
38 | "url": "https://github.com/ElMassimo/iles/issues"
39 | },
40 | "devDependencies": {
41 | "preact": "^10.24.3",
42 | "preact-render-to-string": "^6.5.11",
43 | "solid-js": "^1.9.3",
44 | "svelte": "^5.1.13",
45 | "tsup": "8.2.4",
46 | "typescript": "^5.6.3",
47 | "vite": "^5.4.10",
48 | "vue": "^3.5.12"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/hydration/preact.ts:
--------------------------------------------------------------------------------
1 | import { h, render, toChildArray } from 'preact'
2 | import type { FunctionComponent as Component } from 'preact'
3 | import type { Props, Slots } from './types'
4 | import { onDispose } from './hydration'
5 |
6 | export default function createIsland (component: Component, id: string, el: Element, props: Props, slots: Slots | undefined) {
7 | render(createElement(component, props, slots), el)
8 |
9 | if (import.meta.env.DISPOSE_ISLANDS)
10 | onDispose(id, () => render(null, el))
11 |
12 | if (import.meta.env.DEV)
13 | (window as any).__ILE_DEVTOOLS__?.onHydration({ id, el, props, slots, component, framework: 'preact' })
14 | }
15 |
16 | /**
17 | * Preact doesn't have an equivalent for createStaticVNode.
18 | */
19 | const IslandContent = (props: any) => {
20 | return h('iles-content', { dangerouslySetInnerHTML: { __html: props.content } })
21 | }
22 | IslandContent.shouldComponentUpdate = () => false
23 |
24 | export function createElement (component: Component, props: Props, slots: Slots | undefined) {
25 | const content = slots?.default
26 | const children = content ? toChildArray(h(IslandContent, { content })) : null
27 | return h(component, props, children)
28 | }
29 |
--------------------------------------------------------------------------------
/packages/hydration/solid.ts:
--------------------------------------------------------------------------------
1 | import { hydrate, render, createComponent } from 'solid-js/web'
2 | import type { Component } from 'solid-js'
3 | import type { Props, Slots } from './types'
4 | import { onDispose } from './hydration'
5 |
6 | export default function createIsland (component: Component, id: string, el: Element, props: Props, slots: Slots | undefined) {
7 | if (import.meta.env.DEV)
8 | // @ts-ignore
9 | window._$HY ||= { events: [], completed: new WeakSet(), r: {} }
10 |
11 | const dispose = (import.meta.env.DEV ? render : hydrate)(() => createComponent(component, { ...props, children: createContent(slots) }), el, { renderId: id })
12 |
13 | if (import.meta.env.DISPOSE_ISLANDS)
14 | onDispose(id, dispose)
15 |
16 | if (import.meta.env.DEV)
17 | (window as any).__ILE_DEVTOOLS__?.onHydration({ id, el, props, slots, component, framework: 'solid' })
18 | }
19 |
20 | function createContent (slots: Slots | undefined) {
21 | if (!slots?.default) return
22 | const content = document.createElement('iles-content')
23 | content.innerHTML = slots.default
24 | return Array.from(content.childNodes)
25 | }
26 |
--------------------------------------------------------------------------------
/packages/hydration/svelte.ts:
--------------------------------------------------------------------------------
1 | import { createRawSnippet, mount, unmount, type Snippet } from 'svelte'
2 | import type { Props, Slots } from './types'
3 | import { onDispose } from './hydration'
4 |
5 | type Component = any
6 |
7 | export default function createIsland (Component: Component, id: string, el: Element, props: Props, slots: Slots | undefined = {}) {
8 | let children
9 | let $$slots: Record & { default?: boolean } | undefined
10 | let renderFns: Record = {}
11 |
12 | Object.entries(slots).forEach(([slotName, html]) => {
13 | const fnName = slotName === 'default' ? 'children' : slotName
14 | renderFns[fnName] = createRawSnippet(() => ({ render: () => html }))
15 |
16 | $$slots ??= {}
17 | if (slotName === 'default') {
18 | $$slots.default = true
19 | children = renderFns[fnName]
20 | }
21 | else {
22 | $$slots[fnName] = renderFns[fnName]
23 | }
24 | })
25 |
26 |
27 | const component = mount(Component, {
28 | target: el,
29 | props: {
30 | ...props,
31 | children,
32 | $$slots,
33 | ...renderFns,
34 | },
35 | });
36 |
37 | if (import.meta.env.DISPOSE_ISLANDS)
38 | onDispose(id, () => unmount(component))
39 |
40 | if (import.meta.env.DEV)
41 | (window as any).__ILE_DEVTOOLS__?.onHydration({ id, el, props, slots, component, framework: 'svelte' })
42 | }
43 |
--------------------------------------------------------------------------------
/packages/hydration/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "esnext",
4 | "target": "esnext",
5 | "lib": ["esnext", "DOM"],
6 | "esModuleInterop": true,
7 | "strict": true,
8 | "strictNullChecks": true,
9 | "moduleResolution": "Node",
10 | "resolveJsonModule": true,
11 | "skipLibCheck": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/hydration/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'esnext',
6 | splitting: true,
7 | format: ['esm'],
8 | external: [
9 | 'vue',
10 | 'preact',
11 | 'preact-render-to-string',
12 | 'solid-js',
13 | 'solid-js/web',
14 | 'solid-js/web/dist/web.js',
15 | 'solid-js/web/dist/server.js',
16 | 'svelte',
17 | 'svelte/server',
18 | ],
19 | }
20 |
--------------------------------------------------------------------------------
/packages/hydration/types.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import type Vue from './vue'
4 |
5 | export type Framework = 'vue' | 'preact' | 'solid' | 'svelte' | 'vanilla'
6 | export type FrameworkFn = typeof Vue
7 | export type AsyncFrameworkFn = () => Promise
8 | export type Component = any
9 | export type AsyncComponent = () => Component
10 | export type Id = string
11 | export type Props = Record
12 | export type Slots = Record
13 |
--------------------------------------------------------------------------------
/packages/hydration/vanilla.ts:
--------------------------------------------------------------------------------
1 | import type { Component, Props, Slots } from './types'
2 | import { onDispose } from './hydration'
3 |
4 | type MaybeAsync = T | Promise
5 | export type OnDisposeFn = () => void
6 | export type OnLoadFn = (el: Element, props: Props, slots: Slots | undefined) => MaybeAsync
7 |
8 | const isFunction = (val: any): val is Function => typeof val === 'function'
9 |
10 | // Internal: Calls the function to run custom client code.
11 | export default async function createIsland (component: Component | OnLoadFn, id: string, el: Element, props: Props, slots: Slots | undefined) {
12 | if (isFunction(component)) {
13 | const dispose = await component(el, props, slots)
14 |
15 | if (import.meta.env.DISPOSE_ISLANDS && isFunction(dispose))
16 | onDispose(id, dispose)
17 | }
18 |
19 | if (import.meta.env.DEV)
20 | (window as any).__ILE_DEVTOOLS__?.onHydration({ id, el, props, slots, component, framework: 'none' })
21 | }
22 |
--------------------------------------------------------------------------------
/packages/hydration/vue.ts:
--------------------------------------------------------------------------------
1 | import { h, createApp as createClientApp, createStaticVNode, createSSRApp } from 'vue'
2 | import type { DefineComponent as Component, Component as App } from 'vue'
3 | import type { Props, Slots } from './types'
4 | import { onDispose } from './hydration'
5 |
6 | const createVueApp = import.meta.env.SSR ? createSSRApp : createClientApp
7 |
8 | // Internal: Creates a Vue app and mounts it on the specified island root.
9 | export default function createVueIsland (component: Component, id: string, el: Element, props: Props, slots: Slots | undefined) {
10 | const slotFns = slots && Object.fromEntries(Object.entries(slots).map(([slotName, content]) => {
11 | return [slotName, () => (createStaticVNode as any)(content)]
12 | }))
13 |
14 | const appDefinition: App = { render: () => h(component, props, slotFns) }
15 |
16 | if (import.meta.env.DEV)
17 | appDefinition.name = `Island: ${nameFromFile(component.__file)}`
18 |
19 | const app = createVueApp(appDefinition)
20 | app.mount(el!, Boolean(slots))
21 |
22 | if (import.meta.env.DISPOSE_ISLANDS)
23 | onDispose(id, app.unmount)
24 |
25 | if (import.meta.env.DEV)
26 | (window as any).__ILE_DEVTOOLS__?.onHydration({ id, el, props, slots, component })
27 | }
28 |
29 | function nameFromFile (file?: string) {
30 | const regex = /(\w+?)(?:\.vue)?$/
31 | return file?.match(regex)?.[1] || file
32 | }
33 |
--------------------------------------------------------------------------------
/packages/icons/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [0.10.0-beta.1](https://github.com/ElMassimo/iles/compare/icons@0.8.0...icons@0.10.0-beta.1) (2024-12-04)
2 |
3 |
4 | ### Features
5 |
6 | * update dependencies (latest vite) ([#281](https://github.com/ElMassimo/iles/issues/281)) ([c291852](https://github.com/ElMassimo/iles/commit/c29185255e41e63830236ceb4c67de599aae2012))
7 |
8 |
9 |
10 | # [0.8.0](https://github.com/ElMassimo/iles/compare/icons@0.1.1...icons@0.8.0) (2022-07-14)
11 |
12 |
13 |
14 | ## [0.1.1](https://github.com/ElMassimo/iles/compare/icons@0.1.0...icons@0.1.1) (2021-11-03)
15 |
16 |
17 | ### Bug Fixes
18 |
19 | * allow manually requiring from CJS applications ([664235d](https://github.com/ElMassimo/iles/commit/664235dc0414fa7c9bb37e9c92bddaca5d01bd6e))
20 |
21 |
22 |
23 | # 0.1.0 (2021-11-02)
24 |
25 | - Initial Version
26 |
27 |
28 |
--------------------------------------------------------------------------------
/packages/icons/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
@islands/icons
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 | [components]: https://iles-docs.netlify.app/guide/development
23 | [unplugin-icons]: https://github.com/antfu/unplugin-icons
24 |
25 | An [îles] module to add and configure [unplugin-icons]:
26 |
27 | - ✨ `autoInstall` enabled by default, and `icon` prefix to prevent conflicts
28 |
29 | - 🧱 configures the `unplugin-vue-components` resolver automatically
30 |
31 | - 🎨 files in the `/icons` dir available as the `app` collection, `",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/ElMassimo/iles"
28 | },
29 | "homepage": "https://github.com/ElMassimo/iles",
30 | "bugs": "https://github.com/ElMassimo/iles/issues",
31 | "dependencies": {
32 | "unplugin-icons": "^0.19.0"
33 | },
34 | "devDependencies": {
35 | "iles": "workspace:*"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/icons/src/icons.mjs:
--------------------------------------------------------------------------------
1 | import mod from '../dist/icons.cjs'
2 |
3 | export default mod.default
4 |
--------------------------------------------------------------------------------
/packages/icons/src/icons.ts:
--------------------------------------------------------------------------------
1 | import type { IlesModule } from 'iles'
2 | import type { Options } from 'unplugin-icons'
3 |
4 | import icons from 'unplugin-icons/vite'
5 | import iconsResolver from 'unplugin-icons/resolver'
6 | import { FileSystemIconLoader } from 'unplugin-icons/loaders'
7 |
8 | interface ModuleOptions extends Options {
9 | resolver?: Parameters[0]
10 | }
11 |
12 | /**
13 | * An iles module that configures unplugin-icons to autoInstall collections and
14 | * be able to use local icons in the /icons or /images directories.
15 | *
16 | * @param options - Optional options to configure the output.
17 | */
18 | export default function IlesIcons (options?: ModuleOptions): IlesModule {
19 | const { resolver, ...iconsOptions } = options || {}
20 |
21 | return {
22 | name: '@islands/icons',
23 | components: {
24 | resolvers: [
25 | iconsResolver({
26 | prefix: 'icon',
27 | customCollections: ['app'],
28 | ...resolver,
29 | }),
30 | ],
31 | },
32 | vite: {
33 | plugins: [
34 | icons({
35 | autoInstall: true,
36 | customCollections: {
37 | app: FileSystemIconLoader('./icons'),
38 | },
39 | ...iconsOptions,
40 | }),
41 | ],
42 | },
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/icons/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | splitting: true,
7 | format: ['cjs'],
8 | }
9 |
--------------------------------------------------------------------------------
/packages/iles/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | src/client/shared.ts
3 | src/node/shared.ts
4 | *.log
5 | .DS_Store
6 | .vite_opt_cache
7 | dist
8 | node_modules
9 | TODOs.md
10 | .vscode
11 | /.iles-ssg-temp
12 |
--------------------------------------------------------------------------------
/packages/iles/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib"
3 | }
--------------------------------------------------------------------------------
/packages/iles/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
iles
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 | [cli]: https://iles-docs.netlify.app/guide#usage
23 | [client library]: https://iles-docs.netlify.app/guide/development#using-page-data
24 |
25 | The main [îles] package, which provides a [cli] and the [client library].
26 |
--------------------------------------------------------------------------------
/packages/iles/bin/iles.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import '../dist/node/cli.js'
4 |
--------------------------------------------------------------------------------
/packages/iles/config.js:
--------------------------------------------------------------------------------
1 | // NOTE: This helper file allows to provide it as a config path to Vite or
2 | // Vitest without using the iles executable.
3 | export default async (env, root = process.cwd()) => {
4 | const { default: IslandsPlugins, resolveConfig, mergeConfig } = await import('./dist/node/index.js')
5 |
6 | const config = await resolveConfig(root)
7 |
8 | return mergeConfig(config.vite, {
9 | plugins: IslandsPlugins(config),
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/packages/iles/index.cjs:
--------------------------------------------------------------------------------
1 | // type utils
2 | module.exports.defineConfig = (config) => config
3 |
4 | // proxy cjs utils (sync functions)
5 | Object.assign(module.exports, require('./dist/node-cjs/publicUtils.cjs'))
6 |
7 | // async functions, can be redirect from ESM build
8 | const asyncFunctions = [
9 | 'build',
10 | 'resolveConfig',
11 | ]
12 | asyncFunctions.forEach((name) => {
13 | module.exports[name] = (...args) =>
14 | import('./dist/node/index.js').then((i) => i[name](...args))
15 | })
16 |
17 | // some sync functions are marked not supported due to their complexity and uncommon usage
18 | const unsupportedCJS = ['default']
19 | unsupportedCJS.forEach((name) => {
20 | module.exports[name] = () => {
21 | throw new Error(
22 | `"${name}" is not supported in CJS build of îles.\nPlease use ESM or dynamic imports \`const { ${name} } = await import('iles')\`.`
23 | )
24 | }
25 | })
26 |
--------------------------------------------------------------------------------
/packages/iles/jsx-dev-runtime.js:
--------------------------------------------------------------------------------
1 | export * from './jsx-runtime'
2 | export { jsx as jsxDEV } from './jsx-runtime'
3 |
--------------------------------------------------------------------------------
/packages/iles/jsx-runtime.js:
--------------------------------------------------------------------------------
1 | import {
2 | defineComponent as defineVueComponent,
3 | resolveComponent,
4 | createVNode,
5 | createStaticVNode as raw,
6 | Fragment,
7 | } from 'vue'
8 |
9 | // Internal: Compatibility layer with the automatic JSX runtime of React.
10 | //
11 | // NOTE: Supports v-slots for consistency with @vue/babel-plugin-jsx.
12 | function jsx (type, { children, 'v-slots': vSlots, ...props }) {
13 | let slots
14 |
15 | if (children) {
16 | // Normalize the default slot into a function returning an array of vnodes.
17 | if (!Array.isArray(children)) children = [children]
18 |
19 | slots = type === Fragment
20 | ? children
21 | : { ...vSlots, default: () => children }
22 | }
23 | else {
24 | // Allow empty fragment expressions.
25 | if (type === Fragment)
26 | return null
27 |
28 | slots = vSlots || null
29 | }
30 |
31 | return createVNode(type, props, slots)
32 | }
33 |
34 | // Internal: Extends it to be a stateful component that can perform prop checks.
35 | function defineComponent (MDXContent, definition) {
36 | return defineVueComponent({
37 | ...definition,
38 | props: {
39 | components: { type: Object },
40 | excerpt: { type: Boolean },
41 | },
42 | render (props) {
43 | if (!props) props = this ? { ...this.$props, ...this.$attrs } : {}
44 | return MDXContent(props)
45 | },
46 | })
47 | }
48 |
49 | export {
50 | Fragment,
51 | jsx,
52 | jsx as jsxs,
53 | defineComponent,
54 | resolveComponent,
55 | raw,
56 | }
57 |
--------------------------------------------------------------------------------
/packages/iles/scripts/copyClient.js:
--------------------------------------------------------------------------------
1 | import { copy } from 'fs-extra'
2 | import { globSync } from 'tinyglobby'
3 |
4 | function toDest(file) {
5 | return file.replace(/src\//, 'dist/')
6 | }
7 |
8 | globSync(['src/client/**']).forEach((file) => {
9 | if (/(\.ts|tsconfig\.json)$/.test(file)) return
10 | copy(file, toDest(file))
11 | })
12 |
--------------------------------------------------------------------------------
/packages/iles/scripts/copyShared.js:
--------------------------------------------------------------------------------
1 | import { copy } from 'fs-extra'
2 | import { globSync } from 'tinyglobby'
3 |
4 | globSync(['src/shared/**/*.ts']).forEach(async (file) => {
5 | await Promise.all([
6 | copy(file, file.replace(/^src\/shared\//, 'src/node/')),
7 | copy(file, file.replace(/^src\/shared\//, 'src/client/'))
8 | ])
9 | })
10 |
--------------------------------------------------------------------------------
/packages/iles/scripts/watchAndCopy.js:
--------------------------------------------------------------------------------
1 | import { copy, remove } from 'fs-extra'
2 | import { watch } from 'chokidar'
3 | import { normalizePath } from 'vite'
4 |
5 | function toClientAndNode (method, file) {
6 | file = normalizePath(file)
7 | if (method === 'copy') {
8 | copy(file, file.replace(/^src\/shared\//, 'src/node/'))
9 | copy(file, file.replace(/^src\/shared\//, 'src/client/'))
10 | }
11 | else if (method === 'remove') {
12 | remove(file.replace(/^src\/shared\//, 'src/node/'))
13 | remove(file.replace(/^src\/shared\//, 'src/client/'))
14 | }
15 | }
16 |
17 | function toDist (file) {
18 | return normalizePath(file).replace(/^src\//, 'dist/')
19 | }
20 |
21 | // copy shared files to the client and node directory whenever they change.
22 | watch('src/shared/**/*.ts')
23 | .on('change', file => toClientAndNode('copy', file))
24 | .on('add', file => toClientAndNode('copy', file))
25 | .on('unlink', file => toClientAndNode('remove', file))
26 |
27 | // copy non ts files, such as an html or css, to the dist directory whenever
28 | // they change.
29 | watch('src/client/**/!(*.ts|tsconfig.json)')
30 | .on('change', file => copy(file, toDist(file)))
31 | .on('add', file => copy(file, toDist(file)))
32 | .on('unlink', file => remove(toDist(file)))
33 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/components/App.vue:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/components/NotFound.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
Page Not Found
23 |
24 |
25 | Path
26 | {{ route.path }}
27 |
28 |
29 | Routes
30 | {{ routes }}
31 |
32 |
33 |
34 |
35 |
64 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/composables/appConfig.ts:
--------------------------------------------------------------------------------
1 | import { inject } from 'vue'
2 |
3 | import type { App, InjectionKey } from 'vue'
4 | import type { AppClientConfig } from '../../shared'
5 |
6 | export const appConfigSymbol: InjectionKey = Symbol('[iles-app-config]')
7 |
8 | export function installAppConfig (app: App, config: AppClientConfig) {
9 | app.provide(appConfigSymbol, config)
10 | }
11 |
12 | export function useAppConfig (): AppClientConfig {
13 | const data = inject(appConfigSymbol)
14 | if (!data) throw new Error('App config not properly injected in app')
15 | return data
16 | }
17 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/composables/islandDefinitions.ts:
--------------------------------------------------------------------------------
1 | import { useSSRContext } from 'vue'
2 | import { useRoute } from 'vue-router'
3 |
4 | import type { IslandDefinition } from '../../shared'
5 |
6 | export function useIslandsForPath (): IslandDefinition[] {
7 | const context = useSSRContext()
8 | if (!context) throw new Error('SSR context not found when rendering islands.')
9 | if (!context.islandsByPath) throw new Error('SSR context is missing islands.')
10 |
11 | const currentRoute = useRoute()
12 |
13 | return (context.islandsByPath[currentRoute.path] ||= [])
14 | }
15 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/composables/mdxComponents.ts:
--------------------------------------------------------------------------------
1 | import type { InjectionKey } from 'vue'
2 | import { inject, provide } from 'vue'
3 |
4 | import type { AppContext, MDXComponents, UserApp } from '../../shared'
5 | import { useAppConfig } from './appConfig'
6 |
7 | export const mdxComponentsKey: InjectionKey = Symbol('[iles-mdx-components]')
8 |
9 | // Public: Allows to globally obtain replacements for built-ins, such as img.
10 | export function useMDXComponents () {
11 | return inject(mdxComponentsKey)
12 | }
13 |
14 | // Public: Allows to globally provide replacements for built-ins, such as img.
15 | export function provideMDXComponents (mdxComponents: MDXComponents) {
16 | const overrideElements = useAppConfig()?.overrideElements
17 | if (overrideElements) {
18 | for (const key in mdxComponents) {
19 | if (key === key.toLowerCase() && !overrideElements.includes(key))
20 | console.warn(`Provided an MDX shortcode for '${key}', but it's being optimized. You must specify '${key}' in mdxComponents in app.ts`)
21 | }
22 | }
23 | provide(mdxComponentsKey, mdxComponents)
24 | }
25 |
26 | export async function installMDXComponents (context: AppContext, { mdxComponents }: UserApp) {
27 | const components = mdxComponents
28 | ? typeof mdxComponents === 'function' ? await mdxComponents(context) : mdxComponents
29 | : {}
30 | context.app.provide(mdxComponentsKey, components)
31 | }
32 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/composables/reactivity.ts:
--------------------------------------------------------------------------------
1 | import { reactive } from 'vue'
2 |
3 | import type { Ref } from 'vue'
4 |
5 | /**
6 | * Converts ref to a reactive value.
7 | *
8 | * @see https://vueuse.org/toReactive
9 | */
10 | export function toReactive (objectRef: Ref): T {
11 | const proxy = new Proxy({}, {
12 | get (_, p, receiver) {
13 | return Reflect.get(objectRef.value, p, receiver)
14 | },
15 | set (_, p, value) {
16 | (objectRef.value as any)[p] = value
17 | return true
18 | },
19 | deleteProperty (_, p) {
20 | return Reflect.deleteProperty(objectRef.value, p)
21 | },
22 | has (_, p) {
23 | return Reflect.has(objectRef.value, p)
24 | },
25 | ownKeys () {
26 | return Object.keys(objectRef.value)
27 | },
28 | getOwnPropertyDescriptor () {
29 | return {
30 | enumerable: true,
31 | configurable: true,
32 | }
33 | },
34 | })
35 |
36 | return reactive(proxy) as T
37 | }
38 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/composables/renderer.ts:
--------------------------------------------------------------------------------
1 | import { useSSRContext } from 'vue'
2 | import type { Framework, PrerenderFn } from '@islands/prerender'
3 |
4 | export function useRenderer (framework: Framework): PrerenderFn | undefined {
5 | const context = useSSRContext()
6 | if (!context) throw new Error('SSR context not found when rendering islands.')
7 | if (!context.renderers) throw new Error('Island renderers are missing in SSR context.')
8 | return context.renderers[framework]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/composables/routerLinks.ts:
--------------------------------------------------------------------------------
1 | // Use vue-router navigation during development even when using anchor tags.
2 |
3 | import { useRouter } from 'vue-router'
4 |
5 | export function useRouterLinks () {
6 | const router = useRouter()
7 |
8 | window.addEventListener(
9 | 'click',
10 | (e) => {
11 | if (e.defaultPrevented) return
12 | const link = (e.target as Element).closest('a')
13 | if (link) {
14 | const { protocol, hostname, pathname, hash, target } = link
15 | const currentUrl = window.location
16 | const extMatch = pathname.match(/\.\w+$/)
17 | // only intercept inbound links
18 | if (
19 | !e.ctrlKey
20 | && !e.shiftKey
21 | && !e.altKey
22 | && !e.metaKey
23 | && target !== '_blank'
24 | && protocol === currentUrl.protocol
25 | && hostname === currentUrl.hostname
26 | && !(extMatch && extMatch[0] !== '.html')
27 | && router.resolve({ path: pathname })?.name !== 'NotFound'
28 | ) {
29 | if (pathname !== currentUrl.pathname || !hash) e.preventDefault()
30 | if (pathname !== currentUrl.pathname) router.push({ path: pathname, hash })
31 | }
32 | }
33 | },
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/head.ts:
--------------------------------------------------------------------------------
1 | import { computed } from 'vue'
2 | import type { AppContext, HeadConfig } from '../shared'
3 |
4 | function notEmpty (val: T | boolean | undefined | null): val is T {
5 | return Boolean(val)
6 | }
7 |
8 | export function defaultHead ({ frontmatter, meta, route, config, site }: AppContext, includeSocialTags: boolean | undefined): HeadConfig {
9 | const title = computed(() => {
10 | const title = frontmatter.title ?? meta.title
11 | return title ? `${title} · ${site.title}` : site.title
12 | })
13 |
14 | const description = computed(() =>
15 | frontmatter.description || site.description)
16 |
17 | const currentUrl = computed(() => `${site.url}${route.path}`)
18 |
19 | const metaTags: HeadConfig['meta'] = [
20 | { charset: 'UTF-8' },
21 | { name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
22 | { name: 'description', content: description },
23 | ]
24 |
25 | if (includeSocialTags !== false) {
26 | metaTags.push(
27 | { property: 'og:url', content: currentUrl },
28 | { property: 'og:site_name', content: site.title },
29 | { property: 'og:title', content: title },
30 | { property: 'og:description', content: description },
31 | { property: 'twitter:domain', content: site.canonical },
32 | { property: 'twitter:title', content: title },
33 | { property: 'twitter:description', content: description },
34 | { property: 'twitter:url', content: currentUrl },
35 | )
36 | }
37 |
38 | return {
39 | title,
40 | meta: metaTags,
41 | link: [
42 | config.sitemap && { rel: 'sitemap', href: `${site.url}/sitemap.xml` },
43 | ].filter(notEmpty),
44 | htmlAttrs: { lang: 'en-US' },
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/hydration.ts:
--------------------------------------------------------------------------------
1 | import { useSSRContext } from 'vue'
2 | import {
3 | hydrateWhenIdle,
4 | hydrateNow,
5 | hydrateOnMediaQuery,
6 | hydrateWhenVisible,
7 | } from '@islands/hydration'
8 |
9 | export function newHydrationId () {
10 | if (import.meta.env.SSR) {
11 | const context = useSSRContext()
12 | context!.hydrationSerialNumber ||= 1
13 | return `ile-${context!.hydrationSerialNumber++}`
14 | }
15 | else if (import.meta.env.DEV) {
16 | return (window as any).__ILE_DEVTOOLS__.nextIslandId()
17 | }
18 | }
19 |
20 | export enum Hydrate {
21 | WhenIdle = 'client:idle',
22 | OnLoad = 'client:load',
23 | MediaQuery = 'client:media',
24 | SkipPrerender = 'client:only',
25 | WhenVisible = 'client:visible',
26 | None = 'client:none',
27 | }
28 |
29 | export const hydrationFns = {
30 | [Hydrate.WhenIdle]: hydrateWhenIdle.name,
31 | [Hydrate.OnLoad]: hydrateNow.name,
32 | [Hydrate.MediaQuery]: hydrateOnMediaQuery.name,
33 | [Hydrate.SkipPrerender]: hydrateNow.name,
34 | [Hydrate.WhenVisible]: hydrateWhenVisible.name,
35 | [Hydrate.None]: hydrateNow.name,
36 | }
37 |
38 | // Internal: Strategies that will hydrate instantly and don't need dynamic imports.
39 | export function isEager (strategy: string) {
40 | return strategy === Hydrate.OnLoad || strategy === Hydrate.SkipPrerender || strategy === Hydrate.None
41 | }
42 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/layout.ts:
--------------------------------------------------------------------------------
1 | import type { RouteLocationNormalizedLoaded } from 'vue-router'
2 | import { shallowRef } from 'vue'
3 | import { pageFromRoute } from './composables/pageData'
4 |
5 | export async function resolveLayout (route: RouteLocationNormalizedLoaded) {
6 | const page = pageFromRoute(route)
7 | try {
8 | const layout = page.layoutFn === false ? false : await page.layoutFn?.()
9 | if (route.meta.layout)
10 | route.meta.layout.value = layout
11 | else
12 | route.meta.layout = shallowRef(layout)
13 | }
14 | catch (error) {
15 | console.error(`Error while fetching '${page?.layoutName}' layout.`)
16 | throw error
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/iles/src/client/app/utils.ts:
--------------------------------------------------------------------------------
1 | export { default as serialize } from '@nuxt/devalue'
2 |
3 | export function mapObject (obj: Record, fn: (i: I, key?: string) => O): Record {
4 | const result: Record = {}
5 | for (let key in obj)
6 | result[key] = fn(obj[key], key)
7 | return result
8 | }
9 |
10 | export async function asyncMapObject (obj: Record, fn: (i: I) => Promise): Promise> {
11 | const result: Record = {}
12 | for (let key in obj)
13 | result[key] = await fn(obj[key])
14 | return result
15 | }
16 |
17 | export function getComponentName ({ displayName, name, _componentTag, __file }: any) {
18 | return displayName || name || _componentTag || nameFromFile(__file)
19 | }
20 |
21 | function nameFromFile (file: string) {
22 | const regex = /[\\/]src(?:[\\/](?:pages|layouts))?[\\/](.*?)(?:\.vue)?$/
23 | return file?.match(regex)?.[1] || file
24 | }
25 |
--------------------------------------------------------------------------------
/packages/iles/src/client/client.d.ts:
--------------------------------------------------------------------------------
1 | import '../../types/client'
2 |
--------------------------------------------------------------------------------
/packages/iles/src/client/index.ts:
--------------------------------------------------------------------------------
1 | // exports in this file are exposed to the client app via 'iles'
2 | // so the user can do `import { usePage } from 'iles'`
3 |
4 | // Generic Types
5 | export type { Router, RouteRecordRaw } from './shared'
6 | export type { VueRenderable } from './app/composables/vueRenderer'
7 |
8 | // Composables
9 | export { useAppConfig } from './app/composables/appConfig'
10 | export { usePage, computedInPage } from './app/composables/pageData'
11 | export { useMDXComponents, provideMDXComponents } from './app/composables/mdxComponents'
12 | export { useVueRenderer } from './app/composables/vueRenderer'
13 | export { useRouter, useRoute } from 'vue-router'
14 | export { useHead } from '@unhead/vue'
15 |
16 | import type { ComponentOptionsWithoutProps, ComputedRef } from 'vue'
17 | import type { UserApp, GetStaticPaths, Document } from '../../types/shared'
18 |
19 | export function useDocuments (globPattern: string): ComputedRef[]> {
20 | throw new Error(`Unresolved useDocuments('${globPattern}')`)
21 | }
22 |
23 | export function defineApp (app: UserApp) {
24 | return app
25 | }
26 |
27 | export function definePageComponent (page: ComponentOptionsWithoutProps & { getStaticPaths?: GetStaticPaths }) {
28 | return page
29 | }
30 |
--------------------------------------------------------------------------------
/packages/iles/src/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": ".",
5 | "outDir": "../../dist/client",
6 | "paths": {
7 | "iles": ["index.ts"]
8 | }
9 | },
10 | "include": ["."]
11 | }
12 |
--------------------------------------------------------------------------------
/packages/iles/src/client/virtual.d.ts:
--------------------------------------------------------------------------------
1 | import '../../types/virtual'
2 |
--------------------------------------------------------------------------------
/packages/iles/src/node/build/build.ts:
--------------------------------------------------------------------------------
1 | import { resolveConfig } from '../config'
2 | import { renderPages } from './render'
3 | import { bundle } from './bundle'
4 | import { bundleIslands } from './islands'
5 | import { writePages } from './write'
6 | import { withSpinner, rm } from './utils'
7 | import { createSitemap } from './sitemap'
8 |
9 | export async function build (root: string) {
10 | const start = Date.now()
11 |
12 | process.env.NODE_ENV = 'production'
13 | const appConfig = await resolveConfig(root, { command: 'build', mode: 'production', isSsrBuild: false })
14 |
15 | rm(appConfig.outDir)
16 |
17 | const bundleResult = await withSpinner('building client + server bundles',
18 | async () => await bundle(appConfig))
19 |
20 | const islandsByPath = Object.create(null)
21 |
22 | const pagesResult = await renderPages(appConfig, islandsByPath, bundleResult)
23 |
24 | await createSitemap(appConfig, pagesResult.routesToRender)
25 |
26 | await withSpinner('building islands bundle',
27 | async () => await bundleIslands(appConfig, islandsByPath))
28 |
29 | const ssgContext = { config: appConfig, pages: pagesResult.routesToRender }
30 |
31 | await appConfig.ssg.onSiteBundled?.(ssgContext)
32 |
33 | await withSpinner('writing pages',
34 | async () => await writePages(appConfig, islandsByPath, pagesResult))
35 |
36 | await appConfig.ssg.onSiteRendered?.(ssgContext)
37 |
38 | rm(appConfig.tempDir)
39 |
40 | console.info(`build complete in ${((Date.now() - start) / 1000).toFixed(2)}s.`)
41 | }
42 |
--------------------------------------------------------------------------------
/packages/iles/src/node/build/rebaseImports.ts:
--------------------------------------------------------------------------------
1 | import { posix } from 'path'
2 | import { init as initESLexer, parse as parseESModules } from 'es-module-lexer'
3 | import MagicString from 'magic-string'
4 | import type { AppConfig } from '../shared'
5 |
6 | export default async function rebaseImports ({ base, assetsDir }: AppConfig, codeStr: string) {
7 | const assetsBase = posix.join(base, assetsDir)
8 | try {
9 | await initESLexer
10 | const imports = parseESModules(codeStr)[0]
11 | const code = new MagicString(codeStr)
12 | imports.forEach(({ s, e, d }) => {
13 | // Skip quotes if dynamic import.
14 | if (d > -1) {
15 | s += 1
16 | e -= 1
17 | }
18 | code.overwrite(s, e, posix.join(assetsBase, code.slice(s, e)), { contentOnly: true })
19 | })
20 | return code.toString()
21 | }
22 | catch (error) {
23 | console.error(error)
24 | return codeStr
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/iles/src/node/build/sitemap.ts:
--------------------------------------------------------------------------------
1 | import { promises as fs } from 'fs'
2 | import { join } from 'pathe'
3 | import type { AppConfig, RouteToRender } from '../shared'
4 | import { withSpinner, warnMark } from './utils'
5 |
6 | export async function createSitemap (config: AppConfig, routesToRender: RouteToRender[]) {
7 | const { outDir, base, siteUrl, ssg: { sitemap } } = config
8 | if (!sitemap) return
9 | if (!siteUrl) return console.warn(warnMark, 'Skipping sitemap. Configure `siteUrl` to enable sitemap generation.')
10 | withSpinner('rendering sitemap', async () => {
11 | const sitemap = sitemapFor(`${siteUrl}${base}`, routesToRender)
12 | await fs.mkdir(outDir, { recursive: true })
13 | await fs.writeFile(join(outDir, 'sitemap.xml'), sitemap, 'utf8')
14 | })
15 | }
16 |
17 | // Internal: Create a sitemap for the rendered pages.
18 | function sitemapFor (siteUrl: string, routesToRender: RouteToRender[]) {
19 | const pageUrls = new Set()
20 |
21 | // look through built pages, only add HTML
22 | for (const route of routesToRender) {
23 | if (!route.outputFilename.endsWith('.html')) continue
24 | if (route.path === '/404') continue
25 | pageUrls.add(route.path)
26 | }
27 |
28 | const sortedUrls = Array.from(pageUrls)
29 | .sort((a, b) => a.localeCompare(b, 'en', { numeric: true }))
30 |
31 | return `
32 |
33 | ${sortedUrls.map(url => ` ${siteUrl + url.slice(1)} \n`).join('')}
34 |
35 | `
36 | }
37 |
--------------------------------------------------------------------------------
/packages/iles/src/node/constants.ts:
--------------------------------------------------------------------------------
1 | import { version } from '../../package.json'
2 |
3 | export const ILES_APP_ENTRY = '/@iles-entry'
4 | export const VERSION = version
5 |
--------------------------------------------------------------------------------
/packages/iles/src/node/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './plugin/plugin'
2 | export { ILES_APP_ENTRY } from './constants'
3 | export { build } from './build/build'
4 | export { resolveConfig } from './config'
5 | export { mergeConfig } from 'vite'
6 | import type { UserConfig } from '../../types/shared'
7 |
8 | export function defineConfig (config: UserConfig) {
9 | return config
10 | }
11 |
--------------------------------------------------------------------------------
/packages/iles/src/node/modules.ts:
--------------------------------------------------------------------------------
1 | export function unwrapDefault (mod: any): T {
2 | return mod?.default ? unwrapDefault(mod.default) : mod
3 | }
4 |
5 | export function importModule (path: string): Promise {
6 | // handle modules in Windows file system
7 | if (process.platform === 'win32') {
8 | // handle D:\path\to\file
9 | if (path.match(/^\w:\\/))
10 | return import(`file:///${path.replace(/\\/g, '/')}`).then(unwrapDefault)
11 |
12 | // handle D:/path/to/file
13 | if (path.match(/^\w:\//))
14 | return import(`file:///${path}`).then(unwrapDefault)
15 | }
16 |
17 | return import(path).then(unwrapDefault)
18 | }
19 |
--------------------------------------------------------------------------------
/packages/iles/src/node/plugin/composables.ts:
--------------------------------------------------------------------------------
1 | import { promises as fs } from 'fs'
2 | import { resolve } from 'pathe'
3 | import { uniq } from './utils'
4 | import { parseImports } from './parse'
5 |
6 | const definitionRegex = /(?:function|const|let|var)\s+(definePageComponent|use(?:Page|Route|Head|Documents)\b)/g
7 | const composableUsageRegex = /\b(definePageComponent|use(?:Page|Route|Head|Documents))\s*\(/g
8 |
9 | const composables = [
10 | 'definePageComponent',
11 | 'useDocuments',
12 | 'useHead',
13 | 'usePage',
14 | 'useRoute',
15 | ]
16 |
17 | export async function autoImportComposables (code: string, id: string): Promise {
18 | const matches = Array.from(code.matchAll(composableUsageRegex))
19 | if (matches.length === 0) return
20 |
21 | const imports = await parseImports(code)
22 | const defined = new Set(Array.from(code.matchAll(definitionRegex)).map(a => a[1]))
23 |
24 | const composables = uniq(matches.map(a => a[1]))
25 | .filter(composable => !defined.has(composable) && !imports[composable])
26 | .join(', ')
27 |
28 | if (composables)
29 | return `${code}\nimport { ${composables} } from "iles"`
30 | }
31 |
32 | export function writeComposablesDTS (root: string) {
33 | fs.writeFile(resolve(root, 'composables.d.ts'), `// generated by iles
34 | // We suggest you to commit this file into source control
35 |
36 | declare global {
37 | ${composables.map(fn => ` const ${fn}: typeof import('iles')['${fn}']`).join('\n')}
38 | }
39 |
40 | export { }
41 | `, 'utf-8')
42 | }
43 |
--------------------------------------------------------------------------------
/packages/iles/src/node/plugin/hmr.ts:
--------------------------------------------------------------------------------
1 | import hash from 'hash-sum'
2 |
3 | export function hmrRuntime (id: string) {
4 | const hmrId = hash(`${id}default`)
5 | return `
6 | _sfc_main.__hmrId = "${hmrId}"
7 | __VUE_HMR_RUNTIME__.createRecord("${hmrId}", _sfc_main)
8 | import.meta.hot.accept(({default: __default}) => {
9 | __VUE_HMR_RUNTIME__.reload("${hmrId}", __default)
10 | })
11 | `
12 | }
13 |
--------------------------------------------------------------------------------
/packages/iles/src/node/plugin/markdown.ts:
--------------------------------------------------------------------------------
1 | import type { ViteDevServer } from 'vite'
2 | import deepEqual from 'deep-equal'
3 | import type { AppConfig } from '../shared'
4 |
5 | let originalTags: string[]
6 |
7 | // Internal: Detects markdown components overriden in the app.
8 | export function detectMDXComponents (code: string, config: AppConfig, server?: ViteDevServer | undefined) {
9 | const mdxComponents = code.match(/\bmdxComponents\b(?:.*?){(.*?)}/s)?.[1]
10 | if (!mdxComponents) return
11 |
12 | const foundTags = Array.from(mdxComponents.matchAll(/\b['"]?(\w+)['"]?:/g)).map(m => m[1])
13 |
14 | if (!originalTags)
15 | originalTags = config.markdown.overrideElements ||= []
16 |
17 | const dynamicElements = Array.from(new Set([...originalTags, ...foundTags])).sort()
18 | if (!deepEqual(dynamicElements, config.markdown.overrideElements)) {
19 | config.markdown.overrideElements = dynamicElements
20 | server?.moduleGraph.invalidateAll()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/iles/src/node/plugin/site.ts:
--------------------------------------------------------------------------------
1 | import type { AppConfig } from '../shared'
2 |
3 | // Internal: Adds the url to the site for convenience, and enables HMR.
4 | export function extendSite (code: string, config: AppConfig) {
5 | return `${code.replace('export default ', 'let __site = ')}
6 | __site.url = '${config.siteUrl}${config.base.slice(0, config.base.length - 1)}'
7 | __site.canonical = '${config.siteUrl.split('//', 2)[1] ?? ''}'
8 | import { ref as _$ref } from 'vue'
9 | const __siteRef = _$ref(__site)
10 | __site = { ref: __siteRef }
11 | export { __site, __siteRef as default }
12 |
13 | if (import.meta.hot)
14 | import.meta.hot.accept(mod => {
15 | __site.ref.value = mod.__site.ref.value
16 | mod.__site.ref = __site.ref
17 | })
18 | `
19 | // NOTE: The last line replaces the ref of the current module with the ref in
20 | // the original module that was made reactive in `installPageData`, so that
21 | // subsequent HMRs are also performed as expected.
22 | }
23 |
--------------------------------------------------------------------------------
/packages/iles/src/node/preview.ts:
--------------------------------------------------------------------------------
1 | import type { ServerOptions, UserConfig as ViteUserConfig } from 'vite'
2 | import { preview as vitePreview, mergeConfig } from 'vite'
3 | import { resolveConfig } from './config'
4 |
5 | export async function preview (root: string = process.cwd(), serverOptions: ServerOptions = {}) {
6 | const config = await resolveConfig(root)
7 | const viteConfig = mergeConfig(config.vite, {
8 | preview: serverOptions,
9 | } as ViteUserConfig)
10 |
11 | const server = await vitePreview(viteConfig)
12 | server.printUrls()
13 | }
14 |
--------------------------------------------------------------------------------
/packages/iles/src/node/publicUtils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Exported sync utils should go here.
3 | * This file will be bundled to ESM and CJS and redirected by ../index.cjs
4 | * Please control the side-effects by checking the ./dist/node-cjs/publicUtils.cjs bundle
5 | */
6 | export { VERSION as version, ILES_APP_ENTRY } from './constants'
7 | export { version as viteVersion, esbuildVersion, rollupVersion, mergeConfig } from 'vite'
8 |
--------------------------------------------------------------------------------
/packages/iles/src/node/server.ts:
--------------------------------------------------------------------------------
1 | import type { ServerOptions, UserConfig as ViteUserConfig } from 'vite'
2 | import { createServer as createViteServer, mergeConfig } from 'vite'
3 | import { resolveConfig } from './config'
4 | import IslandsPlugins from './plugin/plugin'
5 |
6 | export async function createServer (root: string = process.cwd(), serverOptions: ServerOptions = {}) {
7 | const config = await resolveConfig(root)
8 |
9 | const viteConfig = mergeConfig(config.vite, {
10 | plugins: IslandsPlugins(config),
11 | server: serverOptions,
12 | } as ViteUserConfig)
13 |
14 | return {
15 | config,
16 | viteConfig,
17 | server: await createViteServer(viteConfig),
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/iles/src/node/utils.ts:
--------------------------------------------------------------------------------
1 | import { basename, extname } from 'pathe'
2 |
3 | // Internal: Maps the specified path to its corresponding HTML filename.
4 | //
5 | // NOTE: `filename` can be an optional source for the specified path.
6 | export function pathToHtmlFilename (path: string, filename?: string) {
7 | const ext = extname(path)
8 | if (ext) return path
9 | if (!path.endsWith('/') && filename && basename(filename).split('.')[0] === 'index') path += '/'
10 | return path + (path.endsWith('/') ? 'index.html' : '.html')
11 | }
12 |
13 | // Internal: Used when `prettyUrls: false`.
14 | export function explicitHtmlPath (path: string, filename?: string) {
15 | const htmlFilename = pathToHtmlFilename(path, filename)
16 | return htmlFilename.endsWith('/index.html')
17 | ? htmlFilename.replace(/\/index\.html$/, '/')
18 | : htmlFilename
19 | }
20 |
--------------------------------------------------------------------------------
/packages/iles/src/shared/shared.ts:
--------------------------------------------------------------------------------
1 | export type {
2 | AppClientConfig,
3 | AppConfig,
4 | BaseIlesConfig,
5 | Awaited,
6 | ConfigEnv,
7 | CreateAppConfig,
8 | CreateAppFactory,
9 | HeadConfig,
10 | IslandDefinition,
11 | OnLoadFn,
12 | IslandsByPath,
13 | LayoutFactory,
14 | Document,
15 | NamedPlugins,
16 | PageComponent,
17 | PageData,
18 | RawPageMatter,
19 | PageFrontmatter,
20 | PageMeta,
21 | PageProps,
22 | IlesModule,
23 | IlesModuleLike,
24 | IlesModuleOption,
25 | MDXComponents,
26 | RouteMeta,
27 | Router,
28 | RouteRecordRaw,
29 | RouteLocationNormalizedLoaded,
30 | RouterOptions,
31 | AppContext,
32 | GetStaticPaths,
33 | StaticPath,
34 | SSGContext,
35 | RouteToRender,
36 | UserApp,
37 | UserSite,
38 | UserConfig,
39 | ViteOptions,
40 | PreactOptions,
41 | SolidOptions,
42 | SvelteOptions,
43 | } from '../../types/shared'
44 |
--------------------------------------------------------------------------------
/packages/iles/src/shared/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": ".",
5 | "lib": ["esnext", "dom", "dom.iterable"]
6 | },
7 | "include": ["."]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/iles/tests/__snapshots__/site.spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`site > with site url 1`] = `
4 | "
5 | let __site = {
6 | title: 'îles',
7 | }
8 |
9 | __site.url = 'https://example.com/awesome'
10 | __site.canonical = 'example.com'
11 | import { ref as _$ref } from 'vue'
12 | const __siteRef = _$ref(__site)
13 | __site = { ref: __siteRef }
14 | export { __site, __siteRef as default }
15 |
16 | if (import.meta.hot)
17 | import.meta.hot.accept(mod => {
18 | __site.ref.value = mod.__site.ref.value
19 | mod.__site.ref = __site.ref
20 | })
21 | "
22 | `;
23 |
24 | exports[`site > without data 1`] = `
25 | "
26 | let __site = {}
27 |
28 | __site.url = 'http://example.com'
29 | __site.canonical = 'example.com'
30 | import { ref as _$ref } from 'vue'
31 | const __siteRef = _$ref(__site)
32 | __site = { ref: __siteRef }
33 | export { __site, __siteRef as default }
34 |
35 | if (import.meta.hot)
36 | import.meta.hot.accept(mod => {
37 | __site.ref.value = mod.__site.ref.value
38 | mod.__site.ref = __site.ref
39 | })
40 | "
41 | `;
42 |
43 | exports[`site > without site url 1`] = `
44 | "
45 | const site = {
46 | title: 'îles',
47 | }
48 |
49 | let __site = site
50 |
51 | __site.url = ''
52 | __site.canonical = ''
53 | import { ref as _$ref } from 'vue'
54 | const __siteRef = _$ref(__site)
55 | __site = { ref: __siteRef }
56 | export { __site, __siteRef as default }
57 |
58 | if (import.meta.hot)
59 | import.meta.hot.accept(mod => {
60 | __site.ref.value = mod.__site.ref.value
61 | mod.__site.ref = __site.ref
62 | })
63 | "
64 | `;
65 |
--------------------------------------------------------------------------------
/packages/iles/tests/app-config.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, describe, expect } from 'vitest'
2 |
3 | import config from '@islands/app-config'
4 |
5 | describe('app config', () => {
6 | test('site url', async () => {
7 | expect(config.root).toEqual(process.cwd())
8 | expect(config.base).toEqual('/')
9 | expect(config.siteUrl).toEqual('https://example.com')
10 | expect(config.debug).toEqual(true)
11 | expect(config.jsx).toEqual(undefined)
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/packages/iles/tests/exports.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, describe, expect } from 'vitest'
2 |
3 | import Island from '@components/Island.vue'
4 |
5 | describe('exports', () => {
6 | test('ensure island component can be resolved', () => {
7 | expect(Island.name).toEqual('Island')
8 | })
9 | })
10 |
--------------------------------------------------------------------------------
/packages/iles/tests/layouts.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, describe, expect } from 'vitest'
2 |
3 | import layout from '/src/layouts/default.vue'
4 |
5 | describe('layouts', () => {
6 | test('stub default layout', () => {
7 | expect(layout.name).toEqual('DefaultLayout')
8 | })
9 | })
10 |
--------------------------------------------------------------------------------
/packages/iles/tests/not-found.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, describe, expect } from 'vitest'
2 |
3 | import NotFound from '@islands/components/NotFound'
4 |
5 | describe('not found component', () => {
6 | test('resolves to existing component', () => {
7 | expect(NotFound.name).toEqual('NotFound')
8 | })
9 | })
10 |
--------------------------------------------------------------------------------
/packages/iles/tests/pretty-urls.spec.ts:
--------------------------------------------------------------------------------
1 | import { toExplicitHtmlPath } from '@mdx/utils'
2 | import { test, describe, expect, beforeAll } from 'vitest'
3 |
4 | describe('prettyUrls', () => {
5 | const expectExplicitPath = (path: string) => expect(toExplicitHtmlPath(path))
6 |
7 | test('internal urls', () => {
8 | expectExplicitPath('/about/').toEqual('/about/')
9 | expectExplicitPath('/about').toEqual('/about.html')
10 | expectExplicitPath('/about/index.html').toEqual('/about/')
11 | expectExplicitPath('/about/nested.html').toEqual('/about/nested.html')
12 | })
13 |
14 | test('anchor tags', () => {
15 | expectExplicitPath('#contact').toEqual('#contact')
16 |
17 | expectExplicitPath('/about#contact').toEqual('/about.html#contact')
18 | expectExplicitPath('/about/index.html#contact').toEqual('/about/#contact')
19 |
20 | expectExplicitPath('https://example.com/about#contact').toEqual('https://example.com/about#contact')
21 | expectExplicitPath('https://example.com/#contact').toEqual('https://example.com/#contact')
22 | })
23 |
24 | test('internal assets', () => {
25 | expectExplicitPath('/assets/picture.gif').toEqual('/assets/picture.gif')
26 | })
27 |
28 | test('external urls', () => {
29 | expectExplicitPath('https://example.com').toEqual('https://example.com')
30 | expectExplicitPath('https://example.com/').toEqual('https://example.com/')
31 | expectExplicitPath('http://example.com/').toEqual('http://example.com/')
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/packages/iles/tests/resolvers.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, describe, expect } from 'vitest'
2 | import path from 'path'
3 |
4 | import { IlesComponentResolver, IlesLayoutResolver } from '@node/config'
5 | import { ISLAND_COMPONENT_PATH } from '@node/alias'
6 |
7 | const projectRoot = path.resolve(__dirname, '../../..')
8 | const vuePoint = `${projectRoot}/playground/the-vue-point`
9 |
10 | describe('resolvers', () => {
11 | test('can resolve Island and Head', async () => {
12 | const resolve = IlesComponentResolver
13 | expect(resolve('Island')).toEqual({ from: ISLAND_COMPONENT_PATH })
14 | expect(resolve('Head')).toEqual({ name: 'Head', from: '@unhead/vue/components' })
15 | expect(resolve('Something')).toEqual(undefined)
16 | })
17 |
18 | test('can resolve layouts', async () => {
19 | const layoutsDir = path.resolve(vuePoint, 'src/layouts')
20 | const resolve = IlesLayoutResolver({ layoutsDir })
21 |
22 | expect(resolve('DefaultLayout'))
23 | .toEqual({ name: 'default', from: `${layoutsDir}/default.vue` })
24 |
25 | expect(resolve('PostLayout'))
26 | .toEqual({ name: 'default', from: `${layoutsDir}/post.vue` })
27 |
28 | expect(resolve('SomethingElseLayout'))
29 | .toEqual(undefined)
30 |
31 | expect(resolve('Layout')).toEqual(undefined)
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/packages/iles/tests/site.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, describe, expect } from 'vitest'
2 |
3 | import { extendSite } from '@node/plugin/site'
4 |
5 | describe('site', () => {
6 | test('without data', () => {
7 | const extended = extendSite(`
8 | export default {}
9 | `, { siteUrl: 'http://example.com', base: '/' })
10 | expect(extended).toMatchSnapshot()
11 | })
12 |
13 | test('without site url', () => {
14 | const extended = extendSite(`
15 | const site = {
16 | title: 'îles',
17 | }
18 |
19 | export default site
20 | `, { siteUrl: '', base: '/' })
21 | expect(extended).toMatchSnapshot()
22 | })
23 |
24 | test('with site url', () => {
25 | const extended = extendSite(`
26 | export default {
27 | title: 'îles',
28 | }
29 | `, { siteUrl: 'https://example.com', base: '/awesome/' })
30 | expect(extended).toMatchSnapshot()
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/packages/iles/tests/user-app.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, describe, expect } from 'vitest'
2 |
3 | import emptyApp from '@islands/user-app'
4 |
5 | describe('user app', () => {
6 | test('can import empty app', () => {
7 | expect(emptyApp).toEqual({})
8 | })
9 | })
10 |
--------------------------------------------------------------------------------
/packages/iles/tests/utils.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, describe, expect } from 'vitest'
2 |
3 | import { pascalCase, serialize } from '@node/plugin/utils'
4 |
5 | describe('case conversions', () => {
6 | test('pascalCase', () => {
7 | expect(pascalCase('AudioPlayer')).toEqual('AudioPlayer')
8 | expect(pascalCase('audio-player')).toEqual('AudioPlayer')
9 | expect(pascalCase('bx:bx-sun')).toEqual('BxBxSun')
10 | })
11 | })
12 |
13 | describe('serialize', () => {
14 | test('to string', () => {
15 | const audio = '/song.mp3'
16 | const recordedAt = new Date()
17 | const value = { audio, recordedAt }
18 | const serialized = serialize(value)
19 | // eslint-disable-next-line no-new-func
20 | expect(Function(`return ${serialized}`)()).toEqual(value)
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/packages/iles/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "module": "esnext",
5 | "target": "esnext",
6 | "moduleResolution": "node",
7 | "resolveJsonModule": true,
8 | "strict": true,
9 | "declaration": true,
10 | "noUnusedLocals": true,
11 | "skipLibCheck": true,
12 | "esModuleInterop": true,
13 | "useUnknownInCatchVariables": false,
14 | "lib": ["esnext", "DOM"],
15 | "types": ["node", "vite/client", "vitest"],
16 | "paths": {
17 | "src/*": ["src/*"],
18 | "@components/*": ["src/client/app/components/*"],
19 | "@client/*": ["src/client/*"],
20 | "@node/*": ["src/node/*"],
21 | "@mdx/*": ["../mdx/src/*"],
22 | "lib/*": ["src/node/*"],
23 | "shared/*": ["src/shared/*"],
24 | "tests/*": ["__tests__/*"],
25 | "/@shared/*": ["src/client/shared/*"],
26 | "iles": ["types/index.d.ts"]
27 | }
28 | },
29 | "include": ["src", "tests", "types"],
30 | "exclude": ["dist"]
31 | }
32 |
--------------------------------------------------------------------------------
/packages/iles/tsup-cjs.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | dts: true,
4 | target: 'node20',
5 | splitting: false,
6 | sourcemap: false,
7 | format: ['cjs'],
8 | outDir: 'dist/node-cjs',
9 | external: [
10 | '@vue/runtime-dom/dist/runtime-dom.esm-bundler.js',
11 | 'solid-js/web',
12 | 'preact',
13 | 'esbuild',
14 | 'rollup',
15 | 'vite',
16 | '@antfu/install-pkg',
17 | 'fast-glob',
18 | 'preact-render-to-string',
19 | '@vue/server-renderer',
20 | '@islands/hydration/preact',
21 | '@islands/hydration/dist/hydration.js',
22 | '@islands/hydration/dist/vue.js',
23 | '@islands/hydration/dist/vanilla.js',
24 | '@islands/hydration/dist/solid.js',
25 | '@islands/hydration/dist/preact.js',
26 | '@islands/hydration/dist/svelte.js',
27 | ],
28 | }
29 |
--------------------------------------------------------------------------------
/packages/iles/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | dts: true,
4 | target: 'node20',
5 | splitting: true,
6 | sourcemap: false,
7 | format: ['esm'],
8 | outDir: 'dist/node',
9 | external: [
10 | '@vue/runtime-dom/dist/runtime-dom.esm-bundler.js',
11 | 'solid-js/web',
12 | 'esbuild',
13 | 'rollup',
14 | 'vite',
15 | 'preact',
16 | '@antfu/install-pkg',
17 | 'fast-glob',
18 | 'preact-render-to-string',
19 | '@vue/server-renderer',
20 | '@islands/hydration/preact',
21 | '@islands/hydration/dist/hydration.js',
22 | '@islands/hydration/dist/vue.js',
23 | '@islands/hydration/dist/vanilla.js',
24 | '@islands/hydration/dist/solid.js',
25 | '@islands/hydration/dist/preact.js',
26 | '@islands/hydration/dist/svelte.js',
27 | ],
28 | }
29 |
--------------------------------------------------------------------------------
/packages/iles/types/client.d.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | PageFrontmatter,
3 | PageMeta,
4 | UserSite,
5 | StaticPath,
6 | Router,
7 | RouteLocationNormalizedLoaded,
8 | } from './shared'
9 |
10 | declare module 'vue-router' {
11 | interface RouteMeta {
12 | layout?: import('vue').Ref
13 | pathVariants?: import('vue').Ref
14 | pathVariantsPromise?: import('vue').ComputedRef>
15 | }
16 | }
17 |
18 | declare module '@vue/runtime-core' {
19 | export interface ComponentCustomProperties {
20 | /**
21 | * The frontmatter of the current page.
22 | */
23 | $frontmatter: PageFrontmatter
24 | /**
25 | * Information about the current page, including href and filename.
26 | */
27 | $meta: PageMeta
28 | /**
29 | * Information about the site as exported in src/site.ts
30 | */
31 | $site: UserSite
32 | /**
33 | * Normalized current location. See {@link RouteLocationNormalizedLoaded}.
34 | */
35 | $route: RouteLocationNormalizedLoaded
36 | /**
37 | * {@link Router} instance used by the application.
38 | */
39 | $router: Router
40 | }
41 | }
42 |
43 | declare global {
44 | interface Window {}
45 | }
46 |
--------------------------------------------------------------------------------
/packages/iles/types/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import Plugin from '../dist/node/plugin/plugin'
4 |
5 | export default Plugin
6 | export * from './shared'
7 | export * from '../dist/client/index'
8 | export * from '../dist/node/index'
9 |
10 | import 'vue-router'
11 | import './virtual'
12 | import './client'
13 |
--------------------------------------------------------------------------------
/packages/iles/types/virtual.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | const comp: import('./shared').PageComponent
3 | export default comp
4 | }
5 |
6 | declare module '*.mdx' {
7 | const comp: import('./shared').PageComponent
8 | export default comp
9 | }
10 |
11 | declare module '@islands/routes' {
12 | import type { RouteRecordRaw } from 'vue-router'
13 | const routes: RouteRecordRaw[]
14 | export default routes
15 | }
16 |
17 | declare module '@islands/app-config' {
18 | const config: import('./shared').AppClientConfig
19 | export default config
20 | }
21 |
22 | declare module '@islands/user-app' {
23 | const config: import('./shared').UserApp
24 | export default config
25 | }
26 |
27 | declare module '@islands/user-site' {
28 | const config: import('vue').Ref
29 | export default config
30 | }
31 |
32 | declare module '@islands/components/NotFound' {
33 | const component: import('vue').Component
34 | export default component
35 | }
36 |
--------------------------------------------------------------------------------
/packages/images/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/images",
3 | "version": "0.10.0-beta.1",
4 | "scripts": {
5 | "dev": "npm run build -- --watch",
6 | "build": "tsup src/images.ts",
7 | "lint": "eslint .",
8 | "lint:fix": "eslint . --fix"
9 | },
10 | "type": "module",
11 | "files": [
12 | "dist",
13 | "src/Picture.vue"
14 | ],
15 | "types": "dist/images.d.ts",
16 | "exports": {
17 | ".": {
18 | "import": "./dist/images.js",
19 | "require": "./dist/images.cjs",
20 | "types": "./dist/images.d.ts"
21 | },
22 | "./package.json": "./package.json"
23 | },
24 | "funding": "https://github.com/sponsors/ElMassimo",
25 | "author": "Máximo Mussini ",
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/ElMassimo/iles"
29 | },
30 | "homepage": "https://github.com/ElMassimo/iles",
31 | "bugs": "https://github.com/ElMassimo/iles/issues",
32 | "dependencies": {
33 | "pathe": "^1.1.2",
34 | "vite-plugin-image-presets": "^0.3.4"
35 | },
36 | "devDependencies": {
37 | "iles": "workspace:*",
38 | "tsup": "8.2.4",
39 | "typescript": "^5.6.3"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/images/src/Picture.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/packages/images/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | format: ['esm', 'cjs'],
7 | }
8 |
--------------------------------------------------------------------------------
/packages/mdx/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
@islands/mdx
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 | [docs]: https://iles-docs.netlify.app
23 | [MDX]: https://github.com/mdx-js/mdx
24 | [frontmatter]: https://iles-docs.netlify.app/guide/markdown#frontmatter-and-meta
25 | [mdx documents]: https://iles-docs.netlify.app/guide/markdown
26 | [resolveComponent]: https://v3.vuejs.org/api/global-api.html#resolvecomponent
27 | [unplugin-vue-components]: https://github.com/antfu/unplugin-vue-components
28 |
29 | An [îles] module that adds support for [MDX documents], powered by [MDX].
30 |
31 | It also injects a [recma][MDX] plugin to resolve Vue components in [MDX documents]:
32 |
33 | - 🌎 you can use globally registered components
34 | - 🧱 [`unplugin-vue-components`][unplugin-vue-components] can statically resolve and import components, so you don't need to manually provide components
35 | - 📝 exposes data from plugins to [`meta`][mdx documents]
36 |
--------------------------------------------------------------------------------
/packages/mdx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/mdx",
3 | "version": "0.10.0-beta.1",
4 | "scripts": {
5 | "dev": "npm run build -- --watch",
6 | "build": "tsup src/mdx.ts",
7 | "tsc": "tsc src/mdx.ts --noEmit --skipLibCheck",
8 | "lint": "eslint .",
9 | "lint:fix": "eslint . --fix"
10 | },
11 | "type": "module",
12 | "files": [
13 | "dist",
14 | "src"
15 | ],
16 | "types": "dist/mdx.d.ts",
17 | "main": "dist/mdx.js",
18 | "exports": {
19 | ".": {
20 | "import": "./dist/mdx.js",
21 | "require": "./src/mdx.cjs"
22 | },
23 | "./package.json": "./package.json"
24 | },
25 | "funding": "https://github.com/sponsors/ElMassimo",
26 | "author": "Máximo Mussini ",
27 | "repository": {
28 | "type": "git",
29 | "url": "https://github.com/ElMassimo/iles"
30 | },
31 | "homepage": "https://github.com/ElMassimo/iles",
32 | "bugs": "https://github.com/ElMassimo/iles/issues",
33 | "dependencies": {
34 | "@mdx-js/mdx": "3.0.1",
35 | "estree-walker": "^3.0",
36 | "hash-sum": "^2.0",
37 | "hast-util-to-html": "^9.0.1",
38 | "remark-frontmatter": "^5.0.0",
39 | "source-map": "^0.7.4",
40 | "unist-util-visit": "^5.0.0"
41 | },
42 | "devDependencies": {
43 | "@types/estree-jsx": "^1.0.5",
44 | "@types/hash-sum": "^1.0",
45 | "@types/hast": "^3.0.4",
46 | "hast-util-raw": "^9.0.4",
47 | "mdast-util-mdx-expression": "^2.0.1",
48 | "tsup": "8.2.4",
49 | "typescript": "^5.6.3",
50 | "unified": "^11.0.5",
51 | "vfile": "^6.0.2",
52 | "vite": "^5.4.10"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/mdx/src/mdx.cjs:
--------------------------------------------------------------------------------
1 | module.exports = (...args) => new Promise((resolve, reject) => {
2 | import('../dist/mdx.js')
3 | .then(m => resolve(m.default(...args)))
4 | .catch(reject)
5 | })
6 |
--------------------------------------------------------------------------------
/packages/mdx/src/mdx.ts:
--------------------------------------------------------------------------------
1 | import remarkFrontmatter from 'remark-frontmatter'
2 |
3 | import type { VFile } from 'vfile'
4 | import recmaPlugin from './recma-plugin'
5 | import mdxPlugins from './mdx-vite-plugins'
6 | import { remarkInternalHrefs } from './remark-internal-hrefs'
7 | import { remarkMdxImages } from './remark-mdx-images'
8 | import { rehypeRawExpressions } from './rehype-raw-expressions'
9 |
10 | /**
11 | * An iles module that injects a recma plugin that transforms MDX to allow
12 | * resolving Vue components statically or at runtime.
13 | */
14 | export function vueMdx (): any {
15 | return {
16 | name: '@islands/mdx',
17 | markdown: {
18 | recmaPlugins: [recmaPlugin],
19 | },
20 | configResolved (config: any) {
21 | const { markdown, prettyUrls, namedPlugins } = config
22 |
23 | markdown.remarkPlugins.unshift(
24 | [remarkFrontmatter, ['yaml', 'toml']],
25 | [remarkMdxImages, markdown],
26 | [remarkInternalHrefs, { prettyUrls }],
27 | )
28 |
29 | markdown.rehypePlugins.push(
30 | [rehypeRawExpressions, markdown],
31 | )
32 |
33 | markdown.recmaPlugins.push(
34 | // NOTE: Expose VFile data added by remark and rehype plugins.
35 | () => (_ast: any, vfile: VFile) => {
36 | const page = namedPlugins.pages.api.pageForFilename(vfile.path)
37 | if (page) Object.assign(page.frontmatter.meta, vfile.data)
38 | },
39 | )
40 |
41 | config.vitePlugins.push(...mdxPlugins(markdown))
42 | },
43 | }
44 | }
45 |
46 | export { vueMdx as default, recmaPlugin }
47 | export * from './types'
48 |
--------------------------------------------------------------------------------
/packages/mdx/src/plugins.ts:
--------------------------------------------------------------------------------
1 | import type { Pluggable } from 'unified'
2 | import type { PluginLike, PluginOption } from './types'
3 |
4 | // Resolve plugins that might need an async import in CJS.
5 | export async function resolvePlugins (plugins: PluginOption[]) {
6 | return compact(await Promise.all(plugins.map(resolvePlugin)))
7 | }
8 |
9 | async function resolvePlugin (plugin: PluginOption): Promise {
10 | if (isString(plugin)) return await importPlugin(plugin)
11 | if (!plugin) return plugin
12 | if (isStringPlugin(plugin)) return await importPlugin(...plugin)
13 | return plugin
14 | }
15 |
16 | async function importPlugin (pkgName: string, ...options: any[]): Promise {
17 | return [await import(pkgName).then(unwrapModule), ...options]
18 | }
19 |
20 | function unwrapModule (mod: any): any {
21 | return mod && mod.default ? unwrapModule(mod.default) : mod
22 | }
23 |
24 | export function isString (val: any): val is string {
25 | return typeof val === 'string'
26 | }
27 |
28 | export function isStringPlugin (val: any): val is [string, any] {
29 | return Array.isArray(val) && isString(val[0])
30 | }
31 |
32 | export function compact (val: (false | undefined | null | T)[]): T[] {
33 | return val.filter(x => x) as T[]
34 | }
35 |
--------------------------------------------------------------------------------
/packages/mdx/src/remark-internal-hrefs.ts:
--------------------------------------------------------------------------------
1 | import type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx'
2 | import type { Root } from 'mdast'
3 | import type { Plugin, Transformer } from 'unified'
4 |
5 | import { visit, SKIP } from 'unist-util-visit'
6 |
7 | import { isJsxElement, isString, toExplicitHtmlPath } from './utils'
8 |
9 | export interface HrefOptions {
10 | prettyUrls?: boolean
11 | }
12 |
13 | type HrefPlugin = Plugin<[HrefOptions?], Root, Root>
14 | type HrefProcessor = Transformer
15 |
16 | /**
17 | * A Remark plugin for converting Markdown images to Mdx images using imports
18 | * for the image source.
19 | */
20 | export const remarkInternalHrefs: HrefPlugin = (options) => {
21 | if (!options?.prettyUrls) return remarkProcessor
22 | }
23 |
24 | const remarkProcessor: HrefProcessor = (ast, vfile) => {
25 | visit(ast, (node) => {
26 | if (node.type === 'link') {
27 | const { url } = node
28 | if (url) node.url = toExplicitHtmlPath(url)
29 | return SKIP
30 | }
31 |
32 | if (isJsxElement(node) && (node.name === 'a' || node.name === 'Link')) {
33 | replaceHrefAttribute(node)
34 | return SKIP
35 | }
36 | })
37 |
38 | function replaceHrefAttribute (node: MdxJsxTextElement | MdxJsxFlowElement) {
39 | for (const attr of node.attributes) {
40 | if (attr.type === 'mdxJsxAttribute' && attr.name === 'href') {
41 | if (isString(attr.value) && attr.value) attr.value = toExplicitHtmlPath(attr.value)
42 | break
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/mdx/src/types.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import type { CompileOptions } from '@mdx-js/mdx'
5 | import type { Pluggable } from 'unified'
6 | import type { VFile } from 'vfile'
7 |
8 | export type PluginLike = null | undefined | false | Pluggable
9 | export type PluginOption = PluginLike | Promise | string | [string, any]
10 |
11 | export interface MarkdownOptions extends Omit {
12 | /**
13 | * Recma plugins that should be used to process files.
14 | */
15 | recmaPlugins?: PluginOption[]
16 |
17 | /**
18 | * Remark plugins that should be used to process files.
19 | */
20 | remarkPlugins?: PluginOption[]
21 |
22 | /**
23 | * Rehype plugins that should be used to process files.
24 | */
25 | rehypePlugins?: PluginOption[]
26 |
27 | /**
28 | * Allows to modify an image src. Useful to customize image processing using
29 | * `vite-imagetools` or other rollup plugins.
30 | */
31 | withImageSrc?: (src: string, file: VFile) => string | void
32 |
33 | /**
34 | * Built-in tags that should not be optimized as HTML.
35 | */
36 | overrideElements?: string[]
37 | }
38 |
--------------------------------------------------------------------------------
/packages/mdx/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { extname } from 'path'
2 | import type { Node } from 'unist'
3 | import type { MdxJsxTextElement, MdxJsxFlowElement } from 'mdast-util-mdx-jsx'
4 |
5 | const urlPattern = /^(https?:)?\//
6 | const externalUrlPattern = /^(https?:)?\/\//
7 |
8 | export function isAbsolute (url: string) {
9 | return urlPattern.test(url)
10 | }
11 |
12 | export function isExternal (url: string) {
13 | return externalUrlPattern.test(url)
14 | }
15 |
16 | export function isJsxElement (node: Node): node is MdxJsxTextElement | MdxJsxFlowElement {
17 | return node.type === 'mdxJsxTextElement' || node.type === 'mdxJsxFlowElement'
18 | }
19 |
20 | export function isString (val: any): val is string {
21 | return typeof val === 'string'
22 | }
23 |
24 | export function toExplicitHtmlPath (url: string) {
25 | if (isExternal(url)) return url
26 |
27 | let [path, anchor] = url.split('#', 2)
28 | if (path === '' || path.endsWith('/')) return url
29 |
30 | const ext = extname(path)
31 | if (ext && ext !== '.html') return url
32 |
33 | path = path.endsWith('.html') ? path.replace(/\/index\.html$/, '/') : `${path}.html`
34 | return anchor ? `${path}#${anchor}` : path
35 | }
36 |
--------------------------------------------------------------------------------
/packages/mdx/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | splitting: true,
7 | format: ['esm'],
8 | }
9 |
--------------------------------------------------------------------------------
/packages/pages/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
@islands/pages
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 | [docs]: https://iles-docs.netlify.app
23 | [pages]: https://iles-docs.netlify.app/guide/development#pages
24 | [frontmatter]: /guide/markdown#frontmatter-and-meta
25 | [routing]: https://iles-docs.netlify.app/guide/routing
26 | [vite-plugin-pages]: https://github.com/hannoeru/vite-plugin-pages
27 |
28 | An [îles] module that provides support for [pages], inspired by [vite-plugin-pages].
29 |
30 | - 🛣 file-based [routing]
31 | - 🎣 hooks to extend [frontmatter] and route data
32 | - 📄 adds support for a [`` block][pages] in Vue single-file components
33 |
34 | ```ts
35 | extendFrontmatter (frontmatter, filename) {
36 | if (filename.includes('/posts/'))
37 | frontmatter.layout = 'post'
38 | },
39 | extendRoute (route) {
40 | if (route.path.startsWith('/posts'))
41 | route.path = path.replace(/[\d-]+/, '') // remove date
42 | },
43 | extendRoutes (routes) {
44 | routes.push({ path: '/custom', name: 'Custom', componentFilename: ... }))
45 | },
46 | ```
47 |
48 | extendFrontmatter is very flexible, you could use it to:
49 |
50 | - Infer the title or date from the filename
51 | - Set a different layout for all pages in a specific dir
52 | - Provide additional data to use in the page, such as `gitLastUpdated`
53 |
54 | ## Acknowledgements
55 |
56 | - [`vite-plugin-pages`][vite-plugin-pages]: Early versions of îles used this wonderful library
57 |
--------------------------------------------------------------------------------
/packages/pages/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/pages",
3 | "version": "0.10.0-beta.1",
4 | "scripts": {
5 | "dev": "npm run build -- --watch",
6 | "build": "tsup src/pages.ts",
7 | "lint": "eslint .",
8 | "lint:fix": "eslint . --fix"
9 | },
10 | "type": "module",
11 | "files": [
12 | "dist",
13 | "src"
14 | ],
15 | "types": "dist/pages.d.ts",
16 | "main": "dist/pages.js",
17 | "exports": {
18 | ".": {
19 | "import": "./dist/pages.js",
20 | "require": "./src/pages.cjs"
21 | },
22 | "./package.json": "./package.json"
23 | },
24 | "funding": "https://github.com/sponsors/ElMassimo",
25 | "author": "Máximo Mussini ",
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/ElMassimo/iles"
29 | },
30 | "homepage": "https://github.com/ElMassimo/iles",
31 | "bugs": "https://github.com/ElMassimo/iles/issues",
32 | "dependencies": {
33 | "debug": "^4.3.5",
34 | "deep-equal": "^2.2.3",
35 | "fast-glob": "^3.3.2",
36 | "gray-matter": "^4.0.3",
37 | "pathe": "^1.1.2"
38 | },
39 | "peerDependencies": {
40 | "vue": "^3.3.4"
41 | },
42 | "devDependencies": {
43 | "@types/deep-equal": "^1.0.4",
44 | "@types/js-yaml": "^4.0.9",
45 | "tsup": "8.2.4",
46 | "typescript": "^5.6.3",
47 | "unified": "^11.0.5",
48 | "vue": "^3.5.12"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/pages/src/hmr.ts:
--------------------------------------------------------------------------------
1 | import type { ViteDevServer, Plugin } from 'vite'
2 | import { debug, slash } from './utils'
3 | import { Awaitable, MODULE_ID, ResolvedOptions, PagesApi } from './types'
4 |
5 | export function handleHMR (api: PagesApi, options: ResolvedOptions, clearRoutes: () => void): Plugin['handleHotUpdate'] {
6 | const server = options.server!
7 |
8 | onPage('add', async (path) => {
9 | const page = await api.addPage(path)
10 | debug.hmr('add %s %O', path, page)
11 | return true
12 | })
13 |
14 | onPage('unlink', (path) => {
15 | api.removePage(path)
16 | debug.hmr('remove', path)
17 | return true
18 | })
19 |
20 | return async (ctx) => {
21 | const path = slash(ctx.file)
22 | if (api.isPage(path)) {
23 | const { changed, needsReload } = await api.updatePage(path)
24 | if (changed) debug.hmr('change', path)
25 | if (needsReload) fullReload()
26 | }
27 | }
28 |
29 | function onPage (eventName: string, handler: (path: string) => Awaitable) {
30 | server.watcher.on(eventName, async (path) => {
31 | path = slash(path)
32 | if (api.isPage(path) && await handler(path))
33 | fullReload()
34 | })
35 | }
36 |
37 | function fullReload () {
38 | invalidatePagesModule(server)
39 | clearRoutes()
40 | server.ws.send({ type: 'full-reload' })
41 | }
42 | }
43 |
44 | function invalidatePageFiles (path: string, { moduleGraph }: ViteDevServer) {
45 | moduleGraph.getModulesByFile(path)
46 | ?.forEach(mod => moduleGraph.invalidateModule(mod))
47 | }
48 |
49 | function invalidatePagesModule ({ moduleGraph }: ViteDevServer) {
50 | const mod = moduleGraph.getModuleById(MODULE_ID)
51 | if (mod) moduleGraph.invalidateModule(mod)
52 | }
53 |
--------------------------------------------------------------------------------
/packages/pages/src/pages.cjs:
--------------------------------------------------------------------------------
1 | module.exports = (...args) => new Promise((resolve, reject) => {
2 | import('../dist/pages.js')
3 | .then(m => resolve(m.default(...args)))
4 | .catch(reject)
5 | })
6 |
--------------------------------------------------------------------------------
/packages/pages/src/utils.ts:
--------------------------------------------------------------------------------
1 | import Debug from 'debug'
2 |
3 | export const debug = {
4 | hmr: Debug('iles:pages:hmr'),
5 | }
6 |
7 | export function slash (path: string): string {
8 | return path.replace(/\\/g, '/')
9 | }
10 |
--------------------------------------------------------------------------------
/packages/pages/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | splitting: true,
7 | format: ['esm'],
8 | }
9 |
--------------------------------------------------------------------------------
/packages/prerender/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
@islands/prerender
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 |
23 | Internal utility for [îles] to prerender Vue, Svelte, Preact, and Solid components.
24 |
--------------------------------------------------------------------------------
/packages/prerender/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/prerender",
3 | "description": "Prerender utilities for îles",
4 | "version": "0.10.0-beta.1",
5 | "type": "module",
6 | "files": [
7 | "dist"
8 | ],
9 | "types": "dist/prerender.d.ts",
10 | "module": "dist/prerender.js",
11 | "main": "dist/prerender.js",
12 | "scripts": {
13 | "dev": "npm run build -- --watch",
14 | "build": "tsup prerender.ts",
15 | "lint": "eslint .",
16 | "lint:fix": "eslint . --fix"
17 | },
18 | "exports": {
19 | ".": "./dist/prerender.js",
20 | "./package.json": "./package.json"
21 | },
22 | "keywords": [
23 | "vite",
24 | "vue",
25 | "islands",
26 | "ssg"
27 | ],
28 | "author": "Máximo Mussini",
29 | "license": "MIT",
30 | "homepage": "https://github.com/ElMassimo/iles",
31 | "bugs": {
32 | "url": "https://github.com/ElMassimo/iles/issues"
33 | },
34 | "dependencies": {
35 | "@islands/hydration": "workspace:^0.10.0-beta.1"
36 | },
37 | "devDependencies": {
38 | "preact": "^10.24.3",
39 | "preact-render-to-string": "^6.5.11",
40 | "solid-js": "^1.9.3",
41 | "svelte": "^5.1.13",
42 | "tsup": "8.2.4",
43 | "typescript": "^5.6.3",
44 | "vite": "^5.4.10",
45 | "vue": "^3.5.12"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/prerender/prerender.ts:
--------------------------------------------------------------------------------
1 | import type { Props, Slots, Framework } from '@islands/hydration'
2 |
3 | export type { Framework }
4 |
5 | export type PrerenderFn =
6 | (component: any, props: Props, slots: Slots | undefined, id: string) => Promise
7 |
8 | const _imports: {
9 | preact?: [
10 | typeof import('@islands/hydration/preact'),
11 | typeof import('preact-render-to-string'),
12 | ]
13 | solid?: typeof import('solid-js/web')
14 | } = {}
15 |
16 | export const renderers: Record = {
17 | async preact (component, props, slots) {
18 | const [
19 | { createElement },
20 | { renderToString },
21 | ] = _imports.preact ||= await Promise.all([
22 | import('@islands/hydration/preact'),
23 | import('preact-render-to-string'),
24 | ])
25 | const node = createElement(component, props, slots)
26 | return renderToString(node as any)
27 | },
28 | async solid (component, props, slots, renderId) {
29 | const { ssr, renderToString, createComponent } = _imports.solid ||= await import('solid-js/web')
30 | return renderToString(() => {
31 | const children = slots?.default && ssr(slots.default)
32 | return createComponent(component, { ...props, children })
33 | }, { renderId })
34 | },
35 | async svelte (component, props, slots, renderId) {
36 | const renderSvelteComponent = (await import('./svelte')).default
37 | return renderSvelteComponent(component, props, slots, renderId)
38 | },
39 | async vanilla () {
40 | throw new Error('The vanilla strategy does not prerender islands.')
41 | },
42 | async vue () {
43 | throw new Error('The vue strategy prerenders islands directly in the main app.')
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/packages/prerender/svelte.ts:
--------------------------------------------------------------------------------
1 | import { createRawSnippet, type Snippet } from 'svelte'
2 | import { render } from 'svelte/server'
3 | import type { PrerenderFn } from './prerender'
4 |
5 | const renderSvelteComponent: PrerenderFn = async (Component, props, slots, _id) => {
6 | let children;
7 | let $$slots: Record & { default?: boolean } | undefined
8 | let renderFns: Record = {}
9 |
10 | slots && Object.entries(slots).forEach(([slotName, html]) => {
11 | const fnName = slotName === 'default' ? 'children' : slotName
12 | renderFns[fnName] = createRawSnippet(() => ({ render: () => html }))
13 |
14 | $$slots ??= {}
15 | if (slotName === 'default') {
16 | $$slots.default = true
17 | children = renderFns[fnName]
18 | }
19 | else {
20 | $$slots[fnName] = renderFns[fnName]
21 | }
22 | })
23 |
24 | return render(Component, {
25 | props: {
26 | ...props,
27 | children,
28 | $$slots,
29 | ...renderFns,
30 | },
31 | }).body
32 | }
33 |
34 | export default renderSvelteComponent
35 |
--------------------------------------------------------------------------------
/packages/prerender/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "esnext",
4 | "target": "esnext",
5 | "lib": ["esnext", "DOM"],
6 | "esModuleInterop": true,
7 | "strict": true,
8 | "strictNullChecks": true,
9 | "moduleResolution": "Node",
10 | "resolveJsonModule": true,
11 | "skipLibCheck": true,
12 | "noUnusedParameters": true
13 | },
14 | "exclude": [
15 | "node_modules",
16 | "examples",
17 | "dist"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/packages/prerender/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | format: ['esm'],
7 | external: [
8 | 'vue',
9 | 'preact',
10 | '@islands/hydration/preact',
11 | 'preact-render-to-string',
12 | 'solid-js',
13 | 'solid-js/web',
14 | 'solid-js/web/dist/web.js',
15 | 'solid-js/web/dist/server.js',
16 | 'svelte',
17 | 'svelte/server',
18 | ],
19 | }
20 |
--------------------------------------------------------------------------------
/packages/prism/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [0.10.0-beta.1](https://github.com/ElMassimo/iles/compare/prism@0.8.0...prism@0.10.0-beta.1) (2024-12-04)
2 |
3 |
4 | ### Features
5 |
6 | * update dependencies (latest vite) ([#281](https://github.com/ElMassimo/iles/issues/281)) ([c291852](https://github.com/ElMassimo/iles/commit/c29185255e41e63830236ceb4c67de599aae2012))
7 |
8 |
9 |
10 | # [0.8.0](https://github.com/ElMassimo/iles/compare/prism@0.1.1...prism@0.8.0) (2022-07-14)
11 |
12 |
13 | ### Features
14 |
15 | * convert to ESM and add support for Vite 3 ([#147](https://github.com/ElMassimo/iles/issues/147)) ([7e397b9](https://github.com/ElMassimo/iles/commit/7e397b908746cd8ec875da2a636ae667ae98cb30))
16 |
17 |
18 |
19 | ## [0.1.1](https://github.com/ElMassimo/iles/compare/prism@0.1.0...prism@0.1.1) (2021-12-28)
20 |
21 |
22 | ### Features
23 |
24 | * hoist static MDX content instead of creating vnodes ([#66](https://github.com/ElMassimo/iles/issues/66)) ([07a7a36](https://github.com/ElMassimo/iles/commit/07a7a36430c6d97792910e346409027dfe10909b))
25 |
26 |
27 | # 0.1.0 (2021-12-01)
28 |
29 |
30 | ### Features
31 |
32 | * new @islands/prism module with code and line highlighting ([f7416ec](https://github.com/ElMassimo/iles/commit/f7416ec8ea45b10fd199bdb2806ea54373ec2bf9))
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/packages/prism/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/prism",
3 | "version": "0.10.0-beta.1",
4 | "scripts": {
5 | "dev": "npm run build -- --watch",
6 | "build": "tsup src/prism.ts",
7 | "lint": "eslint .",
8 | "lint:fix": "eslint . --fix"
9 | },
10 | "type": "module",
11 | "files": [
12 | "dist",
13 | "src"
14 | ],
15 | "types": "dist/prism.d.ts",
16 | "exports": {
17 | ".": {
18 | "import": "./dist/prism.js",
19 | "require": "./src/prism.cjs"
20 | },
21 | "./package.json": "./package.json"
22 | },
23 | "funding": "https://github.com/sponsors/ElMassimo",
24 | "author": "Máximo Mussini ",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/ElMassimo/iles"
28 | },
29 | "homepage": "https://github.com/ElMassimo/iles",
30 | "bugs": "https://github.com/ElMassimo/iles/issues",
31 | "dependencies": {
32 | "prismjs": "^1.29.0",
33 | "unist-util-visit": "^5.0.0"
34 | },
35 | "devDependencies": {
36 | "@types/mdast": "^4.0.4",
37 | "@types/prismjs": "^1.26.4",
38 | "iles": "workspace:*",
39 | "tsup": "8.2.4",
40 | "typescript": "^5.6.3",
41 | "unified": "^11.0.5"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/prism/src/prism.cjs:
--------------------------------------------------------------------------------
1 | module.exports = (...args) => new Promise((resolve, reject) => {
2 | import('../dist/prism.js')
3 | .then(m => resolve(m.default(...args)))
4 | .catch(reject)
5 | })
6 |
--------------------------------------------------------------------------------
/packages/prism/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | format: ['esm'],
7 | }
8 |
--------------------------------------------------------------------------------
/packages/pwa/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
@islands/pwa
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [îles]: https://github.com/ElMassimo/iles
22 | [pwa]: https://iles-docs.netlify.app/guide/pwa
23 | [vite-plugin-pwa]: https://github.com/antfu/vite-plugin-pwa
24 |
25 | An [îles] module to add and configure [vite-plugin-pwa], created by @userquin.
26 |
27 | ### Usage 🚀
28 |
29 | See the [_PWA guide_][pwa] for more information.
30 |
31 | ```ts
32 | // iles.config.ts
33 | import { defineConfig } from 'iles'
34 | import pwa from '@islands/pwa'
35 |
36 | export default defineConfig({
37 | modules: [
38 | pwa(options),
39 | ],
40 | })
41 | ```
42 |
--------------------------------------------------------------------------------
/packages/pwa/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@islands/pwa",
3 | "version": "0.10.0-beta.1",
4 | "scripts": {
5 | "dev": "npm run build -- --watch",
6 | "build": "tsup src/pwa.ts",
7 | "lint": "eslint .",
8 | "lint:fix": "eslint . --fix"
9 | },
10 | "files": [
11 | "dist",
12 | "src"
13 | ],
14 | "types": "dist/pwa.d.cts",
15 | "type": "module",
16 | "exports": {
17 | ".": {
18 | "import": "./src/pwa.mjs",
19 | "require": "./dist/pwa.cjs"
20 | },
21 | "./package.json": "./package.json"
22 | },
23 | "funding": "https://github.com/sponsors/userquin",
24 | "authors": [
25 | "Joaquín Sánchez "
26 | ],
27 | "repository": {
28 | "type": "git",
29 | "url": "https://github.com/ElMassimo/iles"
30 | },
31 | "homepage": "https://github.com/ElMassimo/iles",
32 | "bugs": "https://github.com/ElMassimo/iles/issues",
33 | "dependencies": {
34 | "vite-plugin-pwa": "^0.20.5"
35 | },
36 | "devDependencies": {
37 | "iles": "workspace:*"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/pwa/src/pwa.mjs:
--------------------------------------------------------------------------------
1 | import mod from '../dist/pwa.cjs'
2 |
3 | export default mod.default
4 |
--------------------------------------------------------------------------------
/packages/pwa/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from 'tsup'
2 | export const tsup: Options = {
3 | clean: true,
4 | dts: true,
5 | target: 'node20',
6 | splitting: true,
7 | format: ['cjs'],
8 | }
9 |
--------------------------------------------------------------------------------
/playground/the-vue-point/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .vitepress/metadata.json
4 | .iles-ssg-temp
5 |
--------------------------------------------------------------------------------
/playground/the-vue-point/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | îles — french word for "islands"
9 |
10 |
11 | Islands of interactivity with Vue in Vite.js
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | [demo]: https://the-vue-point-with-iles.netlify.app/
25 | [Vue.js official blog]: http://blog.vuejs.org/
26 |
27 | # The Vue Point - with îles
28 |
29 | This is the source code of [the îles port][demo] of the [Vue.js official blog].
30 |
31 | Since it doesn't contain any interactive components, it ships __no JS__.
32 |
33 | [__Live Website__][demo]
34 |
--------------------------------------------------------------------------------
/playground/the-vue-point/composables.d.ts:
--------------------------------------------------------------------------------
1 | // generated by iles
2 | // We suggest you to commit this file into source control
3 |
4 | declare global {
5 | const definePageComponent: typeof import('iles')['definePageComponent']
6 | const useDocuments: typeof import('iles')['useDocuments']
7 | const useHead: typeof import('iles')['useHead']
8 | const usePage: typeof import('iles')['usePage']
9 | const useRoute: typeof import('iles')['useRoute']
10 | }
11 |
12 | export { }
13 |
--------------------------------------------------------------------------------
/playground/the-vue-point/iles.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'iles'
2 |
3 | import excerpt from '@islands/excerpt'
4 | import feed from '@islands/feed'
5 | import headings from '@islands/headings'
6 | import icons from '@islands/icons'
7 | import images, { hdPreset } from '@islands/images'
8 | import prism from '@islands/prism'
9 | import reactivityTransform from '@vue-macros/reactivity-transform/vite'
10 |
11 | import UnoCSS from 'unocss/vite'
12 | import inspect from 'vite-plugin-inspect'
13 |
14 | const presets = {
15 | narrow: hdPreset({
16 | width: 200,
17 | widths: [200],
18 | formats: {
19 | avif: { quality: 44 },
20 | webp: { quality: 44 },
21 | original: {},
22 | },
23 | }),
24 | post: hdPreset({
25 | widths: [440, 758],
26 | formats: {
27 | avif: { quality: 44 },
28 | webp: { quality: 44 },
29 | original: {},
30 | },
31 | }),
32 | }
33 |
34 | export default defineConfig({
35 | siteUrl: 'https://the-vue-point-with-iles.netlify.app/',
36 | turbo: true,
37 | jsx: 'solid',
38 | prettyUrls: false,
39 | svelte: true,
40 | modules: [
41 | excerpt({ separator: ['hr', 'h2', 'excerpt', 'Excerpt'] }),
42 | feed(),
43 | headings(),
44 | icons(),
45 | prism(),
46 | images(presets),
47 | ],
48 | // Example: Configure all posts to use a different layout without having to
49 | // add `layout: 'post'` in every file.
50 | extendFrontmatter (frontmatter, filename) {
51 | if (filename.includes('/posts/'))
52 | frontmatter.layout ||= 'post'
53 | },
54 | markdown: {
55 | withImageSrc (src) {
56 | if (!src.includes('?'))
57 | return `${src}?preset=post`
58 | },
59 | remarkPlugins: ['remark-gfm'],
60 | },
61 | vite: {
62 | plugins: [
63 | reactivityTransform(),
64 | UnoCSS() as any,
65 | Boolean(process.env.DEBUG) && inspect() as any,
66 | ],
67 | },
68 | })
69 |
--------------------------------------------------------------------------------
/playground/the-vue-point/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/playground/the-vue-point/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-blog",
3 | "type": "module",
4 | "version": "1.0.0",
5 | "private": true,
6 | "scripts": {
7 | "dev": "iles",
8 | "build": "iles build",
9 | "preview": "iles preview --open",
10 | "now": "npm run build && npm run preview",
11 | "check": "vue-tsc --noEmit",
12 | "lint": "eslint .",
13 | "lint:fix": "eslint . --fix"
14 | },
15 | "engines": {
16 | "node": "^14.18 || >= 16.0.0"
17 | },
18 | "devDependencies": {
19 | "@iconify-json/carbon": "^1.1.36",
20 | "@islands/excerpt": "workspace:^0.10.0-beta.1",
21 | "@islands/feed": "workspace:*",
22 | "@islands/headings": "workspace:^0.10.0-beta.1",
23 | "@islands/icons": "workspace:*",
24 | "@islands/images": "workspace:*",
25 | "@islands/prism": "workspace:*",
26 | "@vue-macros/reactivity-transform": "^1.0.4",
27 | "iles": "workspace:*",
28 | "remark-gfm": "^4.0.0",
29 | "unocss": "^0.61.8",
30 | "vue-tsc": "^2.1.10"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/playground/the-vue-point/public/_headers:
--------------------------------------------------------------------------------
1 | /assets/*
2 | cache-control: max-age=31536000
3 | cache-control: immutable
4 |
5 | /logo.svg
6 | cache-control: max-age=31536000
7 | cache-control: immutable
8 |
9 | /favicon.ico
10 | cache-control: max-age=31536000
11 | cache-control: immutable
12 |
--------------------------------------------------------------------------------
/playground/the-vue-point/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ElMassimo/iles/c9e95d729013a8f8f425e894cc7785b954cbcd85/playground/the-vue-point/public/favicon.ico
--------------------------------------------------------------------------------
/playground/the-vue-point/public/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/app.ts:
--------------------------------------------------------------------------------
1 | import { defineApp } from 'iles'
2 |
3 | import 'virtual:uno.css'
4 | import '@unocss/reset/tailwind-compat.css'
5 | import '~/style.css'
6 | import 'prismjs/themes/prism-tomorrow.css'
7 |
8 | export default defineApp({
9 | enhanceApp ({ app, head, router }) {
10 | // Configure the Vue app
11 | },
12 | })
13 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/components/Author.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | Authors
8 |
9 |
19 |
20 |
26 |
32 |
33 | Name
34 | {{ author }}
35 | Twitter
36 |
37 | {{ twitter }}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/components/BackLink.tsx:
--------------------------------------------------------------------------------
1 | /** @jsxImportSource solid-js */
2 |
3 | const BackLink = ({ href, children }: { href: string, children?: any }) =>
4 | ← { children }
5 |
6 | export default BackLink
7 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/components/MetaTags.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/components/NavBarLinks.svelte:
--------------------------------------------------------------------------------
1 |
2 |
GitHub Source
9 |
·
10 |
RSS Feed
11 |
·
12 |
Vuejs.org →
19 |
20 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/components/PostDate.vue:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 | Published on
26 |
27 | {{ formatDate(date) }}
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/images/bench.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:7e1858562aea765b1b409ef8fc13d6e0c2da099887f2709c8da738ee410939ed
3 | size 257001
4 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/images/one-piece.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:3503d5cd26f0312e8d60ec654a66172a81da718ad85b44b8c8b0eabb991cb55f
3 | size 60745
4 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
24 |
25 |
26 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/logic/pagination.ts:
--------------------------------------------------------------------------------
1 | import type { StaticPath } from 'iles'
2 |
3 | export function paginate (items: T[], args: { pageSize?: number; pageParam?: string } = {}): StaticPath[] {
4 | const { pageSize = 10, pageParam = 'page' } = args
5 | const pagesCount = Math.max(1, Math.ceil(items.length / pageSize))
6 | return Array.from({ length: pagesCount }, (_, i) => i + 1)
7 | .map((pageNumber) => {
8 | const firstItem = (pageNumber - 1) * pageSize
9 | return {
10 | params: { [pageParam]: String(pageNumber) },
11 | props: {
12 | items: items.slice(firstItem, firstItem + pageSize),
13 | nextPage: pageNumber !== pagesCount ? pageNumber + 1 : undefined,
14 | prevPage: pageNumber === 1 ? undefined : pageNumber - 1,
15 | },
16 | }
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/logic/posts.ts:
--------------------------------------------------------------------------------
1 | import type { Document, PageComponent } from 'iles'
2 | import { computed } from 'vue'
3 |
4 | export interface Post extends PageComponent {
5 | date: Date
6 | author: string
7 | title: string
8 | twitter: string
9 | }
10 |
11 | function byDate (a: Document, b: Document) {
12 | return Number(new Date(b.date)) - Number(new Date(a.date))
13 | }
14 |
15 | export function getPosts () {
16 | const posts = useDocuments('~/pages/posts')
17 | return computed(() => posts.value.sort(byDate))
18 | }
19 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/pages/404.vue:
--------------------------------------------------------------------------------
1 |
2 | title: Not Found
3 | hideFooter: true
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Not Found
12 |
13 |
14 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/pages/feed.vue:
--------------------------------------------------------------------------------
1 |
2 | path: /feed.rss
3 |
4 |
5 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 | alias: ['/posts']
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
27 |
{{ $site.description }}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/playground/the-vue-point/src/site.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | title: 'The Vue Point',
3 | description: 'Updates, tips & opinions from the maintainers of Vue.js.',
4 | }
5 |
--------------------------------------------------------------------------------
/playground/the-vue-point/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "module": "esnext",
5 | "target": "esnext",
6 | "moduleResolution": "node",
7 | "jsx": "preserve",
8 | "jsxFactory": "h",
9 | "jsxFragmentFactory": "Fragment",
10 | "strict": true,
11 | "declaration": true,
12 | "noUnusedLocals": true,
13 | "skipLibCheck": true,
14 | "esModuleInterop": true,
15 | "lib": ["esnext", "DOM"],
16 | "types": [
17 | "@vue-macros/reactivity-transform/macros-global"
18 | ],
19 | "paths": {
20 | "~/*": ["src/*"]
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/playground/the-vue-point/uno.config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | defineConfig,
3 | presetIcons,
4 | presetTypography,
5 | presetUno,
6 | transformerDirectives,
7 | } from 'unocss'
8 | import transformerVariantGroup from '@unocss/transformer-variant-group'
9 |
10 | export default defineConfig({
11 | transformers: [transformerDirectives(), transformerVariantGroup()],
12 | presets: [
13 | presetUno(),
14 | presetTypography({
15 | cssExtend: {
16 | '.prose pre': {
17 | 'background-color': '#1f2937 !important',
18 | 'color': '#e5e7eb !important',
19 | },
20 | },
21 | }),
22 | presetIcons({
23 | prefix: 'i-', // default prefix
24 | }),
25 | ],
26 | safelist: ['blockquote', 'figure', 'code', 'p', 'a'],
27 | })
28 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - packages/hydration/
3 | - packages/prerender/
4 | - packages/headings/
5 | - packages/images/
6 | - packages/mdx/
7 | - packages/excerpt/
8 | - packages/feed/
9 | - packages/icons/
10 | - packages/pages/
11 | - packages/prism/
12 | - packages/iles/
13 | - packages/pwa/
14 | - playground/the-vue-point/
15 | - docs
16 |
--------------------------------------------------------------------------------
/scripts/changelog.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import fs from 'node:fs'
3 | import minimist from 'minimist'
4 | import { execa } from 'execa'
5 | import { fileURLToPath } from 'url'
6 |
7 | const args = minimist(process.argv.slice(2))
8 | const name = args._[0]?.trim() || 'iles'
9 | const __dirname = path.dirname(fileURLToPath(import.meta.url))
10 |
11 | /**
12 | * @param {string} bin
13 | * @param {string[]} args
14 | * @param {object} opts
15 | */
16 | const run = async (bin, args, opts = {}) => await execa(bin, args, { stdio: 'inherit', ...opts })
17 |
18 | /**
19 | * @param {string} paths
20 | */
21 | const resolve = paths => path.resolve(__dirname, `../packages/${name}/${paths}`)
22 |
23 | const tagPrefix = name === 'iles' ? 'v' : `${name}@`
24 |
25 | async function main () {
26 | await run('npx', [
27 | 'conventional-changelog',
28 | '-p', 'angular',
29 | '-i', resolve('CHANGELOG.md'),
30 | '-s',
31 | '-t', tagPrefix,
32 | '--pkg', resolve('package.json'),
33 | '--commit-path', `./packages/${name}`,
34 | ])
35 | }
36 |
37 | main().catch((err) => {
38 | console.error(err)
39 | })
40 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "esnext",
4 | "target": "esnext",
5 | "moduleResolution": "bundler",
6 | "esModuleInterop": true,
7 | "strict": true,
8 | "skipLibCheck": true,
9 | "noUnusedLocals": true,
10 | "resolveJsonModule": true,
11 | "verbatimModuleSyntax": true,
12 | "jsx": "preserve",
13 | "lib": ["esnext", "dom", "dom.iterable"]
14 | },
15 | "exclude": [
16 | "**/dist",
17 | "**/node_modules",
18 | "**/test"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------