@maizzle/framework 6.0.0-rc.0 → 6.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/maizzle.mjs +1 -1
- package/dist/_virtual/_rolldown/runtime.mjs +32 -0
- package/dist/build.mjs +29 -9
- package/dist/build.mjs.map +1 -1
- package/dist/components/Body.vue +111 -0
- package/dist/components/Button.vue +68 -14
- package/dist/components/CodeBlock.vue +68 -0
- package/dist/components/CodeInline.vue +49 -0
- package/dist/components/Column.vue +86 -0
- package/dist/components/Container.vue +48 -0
- package/dist/components/Divider.vue +28 -0
- package/dist/components/Head.vue +30 -0
- package/dist/components/Heading.vue +28 -0
- package/dist/components/Html.vue +104 -0
- package/dist/components/Image.vue +70 -0
- package/dist/components/Layout.vue +93 -0
- package/dist/components/Link.vue +26 -0
- package/dist/components/Markdown.vue +89 -0
- package/dist/components/Outlook.vue +36 -0
- package/dist/components/Overlap.vue +80 -0
- package/dist/components/Preheader.vue +20 -0
- package/dist/components/Row.vue +91 -0
- package/dist/components/Section.vue +83 -0
- package/dist/components/Spacer.vue +50 -7
- package/dist/components/Text.vue +29 -0
- package/dist/components/Vml.vue +165 -13
- package/dist/composables/renderContext.d.mts +5 -0
- package/dist/composables/renderContext.d.mts.map +1 -1
- package/dist/composables/renderContext.mjs.map +1 -1
- package/dist/composables/usePreviewText.d.mts +24 -0
- package/dist/composables/usePreviewText.d.mts.map +1 -0
- package/dist/composables/usePreviewText.mjs +29 -0
- package/dist/composables/usePreviewText.mjs.map +1 -0
- package/dist/config/index.mjs +25 -1
- package/dist/config/index.mjs.map +1 -1
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +3 -1
- package/dist/node_modules/picomatch/index.mjs +13 -0
- package/dist/node_modules/picomatch/index.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/constants.mjs +174 -0
- package/dist/node_modules/picomatch/lib/constants.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/parse.mjs +1067 -0
- package/dist/node_modules/picomatch/lib/parse.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/picomatch.mjs +304 -0
- package/dist/node_modules/picomatch/lib/picomatch.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/scan.mjs +296 -0
- package/dist/node_modules/picomatch/lib/scan.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/utils.mjs +53 -0
- package/dist/node_modules/picomatch/lib/utils.mjs.map +1 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +24 -7
- package/dist/plugin.mjs.map +1 -1
- package/dist/plugins/postcss/tailwindCleanup.d.mts.map +1 -1
- package/dist/plugins/postcss/tailwindCleanup.mjs +44 -13
- package/dist/plugins/postcss/tailwindCleanup.mjs.map +1 -1
- package/dist/render/createRenderer.d.mts +10 -3
- package/dist/render/createRenderer.d.mts.map +1 -1
- package/dist/render/createRenderer.mjs +147 -10
- package/dist/render/createRenderer.mjs.map +1 -1
- package/dist/render/index.mjs +9 -3
- package/dist/render/index.mjs.map +1 -1
- package/dist/serve.d.mts.map +1 -1
- package/dist/serve.mjs +159 -63
- package/dist/serve.mjs.map +1 -1
- package/dist/server/compatibility.d.mts +1 -2
- package/dist/server/compatibility.d.mts.map +1 -1
- package/dist/server/compatibility.mjs +30 -16
- package/dist/server/compatibility.mjs.map +1 -1
- package/dist/server/email.d.mts +17 -0
- package/dist/server/email.d.mts.map +1 -0
- package/dist/server/email.mjs +41 -0
- package/dist/server/email.mjs.map +1 -0
- package/dist/server/linter.d.mts +1 -2
- package/dist/server/linter.d.mts.map +1 -1
- package/dist/server/linter.mjs +60 -71
- package/dist/server/linter.mjs.map +1 -1
- package/dist/server/ui/App.vue +205 -69
- package/dist/server/ui/components/ui/checkbox/Checkbox.vue +35 -0
- package/dist/server/ui/components/ui/checkbox/index.ts +1 -0
- package/dist/server/ui/components/ui/command/CommandDialog.vue +1 -1
- package/dist/server/ui/components/ui/command/CommandInput.vue +19 -1
- package/dist/server/ui/components/ui/command/CommandItem.vue +1 -1
- package/dist/server/ui/components/ui/command/CommandList.vue +1 -1
- package/dist/server/ui/components/ui/command/CommandShortcut.vue +1 -1
- package/dist/server/ui/components/ui/dialog/DialogOverlay.vue +9 -1
- package/dist/server/ui/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
- package/dist/server/ui/components/ui/scroll-area/ScrollBar.vue +1 -1
- package/dist/server/ui/components/ui/sheet/SheetContent.vue +1 -1
- package/dist/server/ui/components/ui/sheet/SheetOverlay.vue +9 -1
- package/dist/server/ui/components/ui/sidebar/Sidebar.vue +8 -1
- package/dist/server/ui/components/ui/sidebar/SidebarProvider.vue +1 -1
- package/dist/server/ui/components/ui/sidebar/SidebarTrigger.vue +5 -4
- package/dist/server/ui/components/ui/tags-input/TagsInput.vue +26 -0
- package/dist/server/ui/components/ui/tags-input/TagsInputInput.vue +17 -0
- package/dist/server/ui/components/ui/tags-input/TagsInputItem.vue +19 -0
- package/dist/server/ui/components/ui/tags-input/TagsInputItemDelete.vue +22 -0
- package/dist/server/ui/components/ui/tags-input/TagsInputItemText.vue +17 -0
- package/dist/server/ui/components/ui/tags-input/index.ts +5 -0
- package/dist/server/ui/components/ui/toggle/index.ts +3 -3
- package/dist/server/ui/components/ui/toggle-group/ToggleGroup.vue +1 -1
- package/dist/server/ui/components/ui/toggle-group/ToggleGroupItem.vue +2 -2
- package/dist/server/ui/main.css +20 -20
- package/dist/server/ui/pages/Home.vue +12 -5
- package/dist/server/ui/pages/Preview.vue +506 -216
- package/dist/transformers/entities.d.mts.map +1 -1
- package/dist/transformers/entities.mjs +3 -0
- package/dist/transformers/entities.mjs.map +1 -1
- package/dist/transformers/filters/defaults.d.mts +6 -0
- package/dist/transformers/filters/defaults.d.mts.map +1 -0
- package/dist/transformers/filters/defaults.mjs +78 -0
- package/dist/transformers/filters/defaults.mjs.map +1 -0
- package/dist/transformers/filters/index.d.mts +22 -0
- package/dist/transformers/filters/index.d.mts.map +1 -0
- package/dist/transformers/filters/index.mjs +67 -0
- package/dist/transformers/filters/index.mjs.map +1 -0
- package/dist/transformers/index.d.mts +11 -9
- package/dist/transformers/index.d.mts.map +1 -1
- package/dist/transformers/index.mjs +19 -11
- package/dist/transformers/index.mjs.map +1 -1
- package/dist/transformers/inlineCSS.d.mts +1 -14
- package/dist/transformers/inlineCSS.d.mts.map +1 -1
- package/dist/transformers/inlineCSS.mjs +25 -34
- package/dist/transformers/inlineCSS.mjs.map +1 -1
- package/dist/transformers/purgeCSS.d.mts.map +1 -1
- package/dist/transformers/purgeCSS.mjs +67 -1
- package/dist/transformers/purgeCSS.mjs.map +1 -1
- package/dist/transformers/sixHex.d.mts +16 -0
- package/dist/transformers/sixHex.d.mts.map +1 -0
- package/dist/transformers/sixHex.mjs +30 -0
- package/dist/transformers/sixHex.mjs.map +1 -0
- package/dist/transformers/tailwindcss.d.mts +6 -2
- package/dist/transformers/tailwindcss.d.mts.map +1 -1
- package/dist/transformers/tailwindcss.mjs +52 -28
- package/dist/transformers/tailwindcss.mjs.map +1 -1
- package/dist/types/config.d.mts +459 -17
- package/dist/types/config.d.mts.map +1 -1
- package/dist/types/index.d.mts +2 -2
- package/dist/utils/ast/serializer.d.mts +3 -2
- package/dist/utils/ast/serializer.d.mts.map +1 -1
- package/dist/utils/ast/serializer.mjs +24 -0
- package/dist/utils/ast/serializer.mjs.map +1 -1
- package/dist/utils/detect.d.mts +5 -0
- package/dist/utils/detect.d.mts.map +1 -0
- package/dist/utils/detect.mjs +11 -0
- package/dist/utils/detect.mjs.map +1 -0
- package/node_modules/@clack/core/CHANGELOG.md +87 -4
- package/node_modules/@clack/core/README.md +1 -1
- package/node_modules/@clack/core/dist/index.d.mts +186 -48
- package/node_modules/@clack/core/dist/index.mjs +10 -14
- package/node_modules/@clack/core/dist/index.mjs.map +1 -1
- package/node_modules/@clack/core/package.json +7 -9
- package/node_modules/@clack/prompts/CHANGELOG.md +171 -7
- package/node_modules/@clack/prompts/README.md +66 -3
- package/node_modules/@clack/prompts/dist/index.d.mts +302 -76
- package/node_modules/@clack/prompts/dist/index.mjs +134 -84
- package/node_modules/@clack/prompts/dist/index.mjs.map +1 -1
- package/node_modules/@clack/prompts/package.json +14 -10
- package/node_modules/citty/LICENSE +0 -15
- package/node_modules/citty/README.md +166 -69
- package/node_modules/citty/dist/index.d.mts +88 -56
- package/node_modules/citty/dist/index.mjs +399 -437
- package/node_modules/citty/package.json +28 -35
- package/node_modules/giget/README.md +59 -11
- package/node_modules/giget/dist/THIRD-PARTY-LICENSES.md +205 -0
- package/node_modules/giget/dist/_chunks/giget.mjs +508 -0
- package/node_modules/giget/dist/_chunks/libs/citty.mjs +269 -0
- package/node_modules/giget/dist/_chunks/libs/nypm.d.mts +1 -0
- package/node_modules/giget/dist/_chunks/libs/nypm.mjs +669 -0
- package/node_modules/giget/dist/_chunks/libs/tar.mjs +2931 -0
- package/node_modules/giget/dist/_chunks/rolldown-runtime.mjs +14 -0
- package/node_modules/giget/dist/cli.d.mts +1 -0
- package/node_modules/giget/dist/cli.mjs +89 -111
- package/node_modules/giget/dist/index.d.mts +46 -35
- package/node_modules/giget/dist/index.mjs +2 -22
- package/node_modules/giget/package.json +32 -45
- package/node_modules/maizzle/README.md +140 -0
- package/node_modules/maizzle/bin/maizzle.mjs +5 -0
- package/node_modules/maizzle/dist/commands/new.d.mts +7 -0
- package/node_modules/maizzle/dist/commands/new.mjs +278 -0
- package/node_modules/{@maizzle/cli → maizzle}/dist/index.d.mts +1 -1
- package/node_modules/maizzle/dist/index.mjs +44 -0
- package/node_modules/{commander → maizzle/node_modules/commander}/Readme.md +94 -67
- package/node_modules/{commander → maizzle/node_modules/commander}/lib/argument.js +5 -4
- package/node_modules/{commander → maizzle/node_modules/commander}/lib/command.js +154 -39
- package/node_modules/{commander → maizzle/node_modules/commander}/lib/help.js +77 -39
- package/node_modules/{commander → maizzle/node_modules/commander}/lib/option.js +16 -3
- package/node_modules/{commander → maizzle/node_modules/commander}/package-support.json +4 -1
- package/node_modules/{commander → maizzle/node_modules/commander}/package.json +8 -8
- package/node_modules/{commander → maizzle/node_modules/commander}/typings/index.d.ts +71 -3
- package/node_modules/{@maizzle/cli → maizzle}/package.json +13 -11
- package/node_modules/tinyexec/README.md +49 -3
- package/node_modules/tinyexec/dist/main.d.mts +25 -14
- package/node_modules/tinyexec/dist/main.mjs +148 -100
- package/node_modules/tinyexec/package.json +9 -8
- package/package.json +9 -4
- package/dist/server/ui/components/ui/resizable/ResizableHandle.vue +0 -30
- package/dist/server/ui/components/ui/resizable/ResizablePanel.vue +0 -21
- package/dist/server/ui/components/ui/resizable/ResizablePanelGroup.vue +0 -25
- package/dist/server/ui/components/ui/resizable/index.ts +0 -3
- package/node_modules/@clack/core/dist/index.cjs +0 -15
- package/node_modules/@clack/core/dist/index.cjs.map +0 -1
- package/node_modules/@clack/core/dist/index.d.cts +0 -211
- package/node_modules/@clack/core/dist/index.d.ts +0 -211
- package/node_modules/@clack/prompts/dist/index.cjs +0 -87
- package/node_modules/@clack/prompts/dist/index.cjs.map +0 -1
- package/node_modules/@clack/prompts/dist/index.d.cts +0 -165
- package/node_modules/@clack/prompts/dist/index.d.ts +0 -165
- package/node_modules/@maizzle/cli/README.md +0 -58
- package/node_modules/@maizzle/cli/dist/index.mjs +0 -42
- package/node_modules/citty/dist/index.cjs +0 -475
- package/node_modules/citty/dist/index.d.cts +0 -80
- package/node_modules/citty/dist/index.d.ts +0 -80
- package/node_modules/consola/LICENSE +0 -47
- package/node_modules/consola/README.md +0 -352
- package/node_modules/consola/basic.d.ts +0 -1
- package/node_modules/consola/browser.d.ts +0 -1
- package/node_modules/consola/core.d.ts +0 -1
- package/node_modules/consola/dist/basic.cjs +0 -32
- package/node_modules/consola/dist/basic.d.cts +0 -23
- package/node_modules/consola/dist/basic.d.mts +0 -21
- package/node_modules/consola/dist/basic.d.ts +0 -23
- package/node_modules/consola/dist/basic.mjs +0 -24
- package/node_modules/consola/dist/browser.cjs +0 -84
- package/node_modules/consola/dist/browser.d.cts +0 -23
- package/node_modules/consola/dist/browser.d.mts +0 -21
- package/node_modules/consola/dist/browser.d.ts +0 -23
- package/node_modules/consola/dist/browser.mjs +0 -76
- package/node_modules/consola/dist/chunks/prompt.cjs +0 -288
- package/node_modules/consola/dist/chunks/prompt.mjs +0 -280
- package/node_modules/consola/dist/core.cjs +0 -517
- package/node_modules/consola/dist/core.d.cts +0 -459
- package/node_modules/consola/dist/core.d.mts +0 -459
- package/node_modules/consola/dist/core.d.ts +0 -459
- package/node_modules/consola/dist/core.mjs +0 -512
- package/node_modules/consola/dist/index.cjs +0 -663
- package/node_modules/consola/dist/index.d.cts +0 -24
- package/node_modules/consola/dist/index.d.mts +0 -22
- package/node_modules/consola/dist/index.d.ts +0 -24
- package/node_modules/consola/dist/index.mjs +0 -651
- package/node_modules/consola/dist/shared/consola.DCGIlDNP.cjs +0 -75
- package/node_modules/consola/dist/shared/consola.DRwqZj3T.mjs +0 -72
- package/node_modules/consola/dist/shared/consola.DXBYu-KD.mjs +0 -288
- package/node_modules/consola/dist/shared/consola.DwRq1yyg.cjs +0 -312
- package/node_modules/consola/dist/utils.cjs +0 -64
- package/node_modules/consola/dist/utils.d.cts +0 -286
- package/node_modules/consola/dist/utils.d.mts +0 -286
- package/node_modules/consola/dist/utils.d.ts +0 -286
- package/node_modules/consola/dist/utils.mjs +0 -54
- package/node_modules/consola/lib/index.cjs +0 -10
- package/node_modules/consola/package.json +0 -136
- package/node_modules/consola/utils.d.ts +0 -1
- package/node_modules/create-maizzle/README.md +0 -86
- package/node_modules/create-maizzle/bin/create-maizzle.mjs +0 -4
- package/node_modules/create-maizzle/node_modules/@clack/core/CHANGELOG.md +0 -340
- package/node_modules/create-maizzle/node_modules/@clack/core/LICENSE +0 -9
- package/node_modules/create-maizzle/node_modules/@clack/core/README.md +0 -22
- package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.d.mts +0 -349
- package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.mjs +0 -11
- package/node_modules/create-maizzle/node_modules/@clack/core/dist/index.mjs.map +0 -1
- package/node_modules/create-maizzle/node_modules/@clack/core/package.json +0 -60
- package/node_modules/create-maizzle/node_modules/@clack/prompts/CHANGELOG.md +0 -576
- package/node_modules/create-maizzle/node_modules/@clack/prompts/LICENSE +0 -9
- package/node_modules/create-maizzle/node_modules/@clack/prompts/README.md +0 -270
- package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.d.mts +0 -391
- package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.mjs +0 -137
- package/node_modules/create-maizzle/node_modules/@clack/prompts/dist/index.mjs.map +0 -1
- package/node_modules/create-maizzle/node_modules/@clack/prompts/package.json +0 -65
- package/node_modules/create-maizzle/package.json +0 -47
- package/node_modules/create-maizzle/src/index.js +0 -242
- package/node_modules/defu/LICENSE +0 -21
- package/node_modules/defu/README.md +0 -171
- package/node_modules/defu/dist/defu.cjs +0 -77
- package/node_modules/defu/dist/defu.d.cts +0 -31
- package/node_modules/defu/dist/defu.d.mts +0 -29
- package/node_modules/defu/dist/defu.d.ts +0 -31
- package/node_modules/defu/dist/defu.mjs +0 -69
- package/node_modules/defu/lib/defu.cjs +0 -10
- package/node_modules/defu/lib/defu.d.cts +0 -12
- package/node_modules/defu/package.json +0 -48
- package/node_modules/giget/dist/shared/giget.OCaTp9b-.mjs +0 -468
- package/node_modules/node-fetch-native/LICENSE +0 -114
- package/node_modules/node-fetch-native/README.md +0 -225
- package/node_modules/node-fetch-native/dist/chunks/multipart-parser.cjs +0 -2
- package/node_modules/node-fetch-native/dist/chunks/multipart-parser.mjs +0 -2
- package/node_modules/node-fetch-native/dist/index.cjs +0 -1
- package/node_modules/node-fetch-native/dist/index.mjs +0 -1
- package/node_modules/node-fetch-native/dist/native.cjs +0 -1
- package/node_modules/node-fetch-native/dist/native.mjs +0 -1
- package/node_modules/node-fetch-native/dist/node.cjs +0 -19
- package/node_modules/node-fetch-native/dist/node.mjs +0 -19
- package/node_modules/node-fetch-native/dist/polyfill.cjs +0 -1
- package/node_modules/node-fetch-native/dist/polyfill.mjs +0 -1
- package/node_modules/node-fetch-native/dist/proxy-stub.cjs +0 -1
- package/node_modules/node-fetch-native/dist/proxy-stub.mjs +0 -1
- package/node_modules/node-fetch-native/dist/proxy.cjs +0 -58
- package/node_modules/node-fetch-native/dist/shared/node-fetch-native.DfbY2q-x.mjs +0 -1
- package/node_modules/node-fetch-native/dist/shared/node-fetch-native.DhEqb06g.cjs +0 -1
- package/node_modules/node-fetch-native/index.d.ts +0 -1
- package/node_modules/node-fetch-native/lib/empty.cjs +0 -0
- package/node_modules/node-fetch-native/lib/empty.mjs +0 -0
- package/node_modules/node-fetch-native/lib/index.cjs +0 -11
- package/node_modules/node-fetch-native/lib/index.d.cts +0 -10
- package/node_modules/node-fetch-native/lib/index.d.mts +0 -10
- package/node_modules/node-fetch-native/lib/index.d.ts +0 -10
- package/node_modules/node-fetch-native/lib/native.cjs +0 -11
- package/node_modules/node-fetch-native/lib/polyfill.d.cts +0 -1
- package/node_modules/node-fetch-native/lib/polyfill.d.mts +0 -1
- package/node_modules/node-fetch-native/lib/polyfill.d.ts +0 -1
- package/node_modules/node-fetch-native/lib/proxy.d.ts +0 -32
- package/node_modules/node-fetch-native/node.d.ts +0 -1
- package/node_modules/node-fetch-native/package.json +0 -138
- package/node_modules/node-fetch-native/polyfill.d.ts +0 -1
- package/node_modules/node-fetch-native/proxy.d.ts +0 -1
- package/node_modules/nypm/node_modules/citty/LICENSE +0 -21
- package/node_modules/nypm/node_modules/citty/README.md +0 -231
- package/node_modules/nypm/node_modules/citty/dist/index.d.mts +0 -112
- package/node_modules/nypm/node_modules/citty/dist/index.mjs +0 -425
- package/node_modules/nypm/node_modules/citty/package.json +0 -42
- /package/node_modules/{nypm/node_modules/citty → citty}/dist/THIRD-PARTY-LICENSES.md +0 -0
- /package/node_modules/{nypm/node_modules/citty → citty}/dist/_chunks/libs/scule.mjs +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/LICENSE +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/component.d.mts +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/component.mjs +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/config.d.mts +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/config.mjs +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/layout.d.mts +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/layout.mjs +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/scaffold.d.mts +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/scaffold.mjs +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/stubs/component.vue +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/stubs/config.ts +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/stubs/layout.vue +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/stubs/template.vue +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/template.d.mts +0 -0
- /package/node_modules/{@maizzle/cli → maizzle}/dist/commands/make/template.mjs +0 -0
- /package/node_modules/{commander → maizzle/node_modules/commander}/LICENSE +0 -0
- /package/node_modules/{commander → maizzle/node_modules/commander}/esm.mjs +0 -0
- /package/node_modules/{commander → maizzle/node_modules/commander}/index.js +0 -0
- /package/node_modules/{commander → maizzle/node_modules/commander}/lib/error.js +0 -0
- /package/node_modules/{commander → maizzle/node_modules/commander}/lib/suggestSimilar.js +0 -0
- /package/node_modules/{commander → maizzle/node_modules/commander}/typings/esm.d.mts +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import nodemailer from "nodemailer";
|
|
2
|
+
|
|
3
|
+
//#region src/server/email.ts
|
|
4
|
+
async function sendEmail(payload, config, templateConfig) {
|
|
5
|
+
const emailConfig = templateConfig.server?.email ?? config.server?.email;
|
|
6
|
+
let transport;
|
|
7
|
+
let isEthereal = false;
|
|
8
|
+
if (emailConfig?.transport) transport = nodemailer.createTransport(emailConfig.transport);
|
|
9
|
+
else {
|
|
10
|
+
const testAccount = await nodemailer.createTestAccount();
|
|
11
|
+
transport = nodemailer.createTransport({
|
|
12
|
+
host: "smtp.ethereal.email",
|
|
13
|
+
port: 587,
|
|
14
|
+
secure: false,
|
|
15
|
+
auth: {
|
|
16
|
+
user: testAccount.user,
|
|
17
|
+
pass: testAccount.pass
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
isEthereal = true;
|
|
21
|
+
}
|
|
22
|
+
const from = emailConfig?.from ?? "Maizzle <maizzle@ethereal.email>";
|
|
23
|
+
const info = await transport.sendMail({
|
|
24
|
+
from,
|
|
25
|
+
to: payload.to.join(", "),
|
|
26
|
+
subject: payload.subject || "Test email",
|
|
27
|
+
html: payload.html,
|
|
28
|
+
text: payload.text || void 0
|
|
29
|
+
});
|
|
30
|
+
const previewUrl = isEthereal ? nodemailer.getTestMessageUrl(info) || void 0 : void 0;
|
|
31
|
+
const recipient = payload.to.length === 1 ? payload.to[0] : `${payload.to.length} recipients`;
|
|
32
|
+
return {
|
|
33
|
+
success: true,
|
|
34
|
+
message: isEthereal ? "Sent via Ethereal" : `Sent to ${recipient}`,
|
|
35
|
+
previewUrl: typeof previewUrl === "string" ? previewUrl : void 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { sendEmail };
|
|
41
|
+
//# sourceMappingURL=email.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.mjs","names":[],"sources":["../../src/server/email.ts"],"sourcesContent":["import nodemailer from 'nodemailer'\nimport type { MaizzleConfig } from '../types/index.ts'\n\nexport interface SendEmailPayload {\n to: string[]\n subject: string\n html: string\n text?: string\n}\n\ninterface SendEmailResponse {\n success: boolean\n message: string\n previewUrl?: string\n}\n\nexport async function sendEmail(\n payload: SendEmailPayload,\n config: MaizzleConfig,\n templateConfig: MaizzleConfig,\n): Promise<SendEmailResponse> {\n // Template-level config takes priority over global config\n const emailConfig = templateConfig.server?.email ?? config.server?.email\n\n let transport: nodemailer.Transporter\n let isEthereal = false\n\n if (emailConfig?.transport) {\n transport = nodemailer.createTransport(emailConfig.transport as any)\n } else {\n // Fallback to Ethereal\n const testAccount = await nodemailer.createTestAccount()\n transport = nodemailer.createTransport({\n host: 'smtp.ethereal.email',\n port: 587,\n secure: false,\n auth: {\n user: testAccount.user,\n pass: testAccount.pass,\n },\n })\n isEthereal = true\n }\n\n const from = emailConfig?.from ?? 'Maizzle <maizzle@ethereal.email>'\n\n const info = await transport.sendMail({\n from,\n to: payload.to.join(', '),\n subject: payload.subject || 'Test email',\n html: payload.html,\n text: payload.text || undefined,\n })\n\n const previewUrl = isEthereal ? nodemailer.getTestMessageUrl(info) || undefined : undefined\n\n const recipient = payload.to.length === 1\n ? payload.to[0]\n : `${payload.to.length} recipients`\n\n return {\n success: true,\n message: isEthereal\n ? 'Sent via Ethereal'\n : `Sent to ${recipient}`,\n previewUrl: typeof previewUrl === 'string' ? previewUrl : undefined,\n }\n}\n"],"mappings":";;;AAgBA,eAAsB,UACpB,SACA,QACA,gBAC4B;CAE5B,MAAM,cAAc,eAAe,QAAQ,SAAS,OAAO,QAAQ;CAEnE,IAAI;CACJ,IAAI,aAAa;AAEjB,KAAI,aAAa,UACf,aAAY,WAAW,gBAAgB,YAAY,UAAiB;MAC/D;EAEL,MAAM,cAAc,MAAM,WAAW,mBAAmB;AACxD,cAAY,WAAW,gBAAgB;GACrC,MAAM;GACN,MAAM;GACN,QAAQ;GACR,MAAM;IACJ,MAAM,YAAY;IAClB,MAAM,YAAY;IACnB;GACF,CAAC;AACF,eAAa;;CAGf,MAAM,OAAO,aAAa,QAAQ;CAElC,MAAM,OAAO,MAAM,UAAU,SAAS;EACpC;EACA,IAAI,QAAQ,GAAG,KAAK,KAAK;EACzB,SAAS,QAAQ,WAAW;EAC5B,MAAM,QAAQ;EACd,MAAM,QAAQ,QAAQ;EACvB,CAAC;CAEF,MAAM,aAAa,aAAa,WAAW,kBAAkB,KAAK,IAAI,SAAY;CAElF,MAAM,YAAY,QAAQ,GAAG,WAAW,IACpC,QAAQ,GAAG,KACX,GAAG,QAAQ,GAAG,OAAO;AAEzB,QAAO;EACL,SAAS;EACT,SAAS,aACL,sBACA,WAAW;EACf,YAAY,OAAO,eAAe,WAAW,aAAa;EAC3D"}
|
package/dist/server/linter.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { MaizzleConfig } from "../types/config.mjs";
|
|
2
1
|
//#region src/server/linter.d.ts
|
|
3
|
-
declare function serveLint(url: string,
|
|
2
|
+
declare function serveLint(url: string, res: any): void;
|
|
4
3
|
//#endregion
|
|
5
4
|
export { serveLint };
|
|
6
5
|
//# sourceMappingURL=linter.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linter.d.mts","names":[],"sources":["../../src/server/linter.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"linter.d.mts","names":[],"sources":["../../src/server/linter.ts"],"mappings":";iBAUgB,SAAA,CAAU,GAAA,UAAa,GAAA"}
|
package/dist/server/linter.mjs
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import { glob } from "tinyglobby";
|
|
4
3
|
|
|
5
4
|
//#region src/server/linter.ts
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const match = (await glob(config.content ?? ["emails/**/*.vue"])).find((t) => t.replace(/\.(vue|md)$/, "") === templateSlug);
|
|
9
|
-
if (!match) {
|
|
10
|
-
res.statusCode = 404;
|
|
11
|
-
res.end(JSON.stringify({ error: "Template not found" }));
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
5
|
+
function serveLint(url, res) {
|
|
6
|
+
const filePath = url.replace("/__maizzle/lint/", "").replace(/\?.*$/, "");
|
|
14
7
|
try {
|
|
15
|
-
const source = readFileSync(resolve(
|
|
8
|
+
const source = readFileSync(resolve(filePath), "utf-8");
|
|
16
9
|
const templateMatch = source.match(/<template\b[^>]*>([\s\S]*)<\/template>/);
|
|
17
10
|
const issues = lintHtml(templateMatch ? templateMatch[1] : source, templateMatch ? source.slice(0, source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1])).split("\n").length - 1 : 0);
|
|
18
11
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -22,98 +15,90 @@ async function serveLint(url, config, res) {
|
|
|
22
15
|
res.end(JSON.stringify({ error: error.message }));
|
|
23
16
|
}
|
|
24
17
|
}
|
|
18
|
+
function lineAt(html, offset, lineOffset) {
|
|
19
|
+
return html.slice(0, offset).split("\n").length + lineOffset;
|
|
20
|
+
}
|
|
25
21
|
function lintHtml(html, lineOffset = 0) {
|
|
26
22
|
const issues = [];
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
for (const match of imgMatches) {
|
|
33
|
-
const tag = match[0];
|
|
23
|
+
for (const m of Array.from(html.matchAll(/<([a-zA-Z][a-zA-Z0-9]*)\b([\s\S]*?)>/g))) {
|
|
24
|
+
const tag = m[0];
|
|
25
|
+
const tagName = m[1].toLowerCase();
|
|
26
|
+
const line = lineAt(html, m.index, lineOffset);
|
|
27
|
+
if (tagName === "img") {
|
|
34
28
|
if (!/\balt\s*=/i.test(tag)) issues.push({
|
|
35
29
|
type: "warning",
|
|
36
30
|
title: "Missing alt text",
|
|
37
31
|
message: "Image is missing the alt attribute",
|
|
38
|
-
line
|
|
32
|
+
line
|
|
39
33
|
});
|
|
40
|
-
|
|
41
|
-
for (const match of imgMatches) {
|
|
42
|
-
const srcMatch = match[0].match(/\bsrc\s*=\s*["']([^"']*)["']/i);
|
|
34
|
+
const srcMatch = tag.match(/\bsrc\s*=\s*["']([^"']*)["']/i);
|
|
43
35
|
if (!srcMatch) issues.push({
|
|
44
36
|
type: "error",
|
|
45
37
|
title: "Missing image src",
|
|
46
38
|
message: "Image tag has no src attribute",
|
|
47
|
-
line
|
|
39
|
+
line
|
|
48
40
|
});
|
|
49
41
|
else if (!srcMatch[1].trim()) issues.push({
|
|
50
42
|
type: "error",
|
|
51
43
|
title: "Empty image src",
|
|
52
44
|
message: "Image src attribute is empty",
|
|
53
|
-
line
|
|
45
|
+
line
|
|
54
46
|
});
|
|
55
47
|
else if (srcMatch[1].trim().startsWith("http:")) issues.push({
|
|
56
48
|
type: "warning",
|
|
57
49
|
title: "Insecure image src",
|
|
58
50
|
message: "Image loads over HTTP instead of HTTPS",
|
|
59
|
-
line
|
|
51
|
+
line
|
|
60
52
|
});
|
|
61
53
|
}
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
if (!
|
|
66
|
-
type: "
|
|
67
|
-
title: "
|
|
68
|
-
message: "
|
|
69
|
-
line
|
|
54
|
+
const hrefMatch = tag.match(/\bhref\s*=\s*["']([^"']*)["']/i);
|
|
55
|
+
if (hrefMatch) {
|
|
56
|
+
const href = hrefMatch[1].trim();
|
|
57
|
+
if (!href) issues.push({
|
|
58
|
+
type: "warning",
|
|
59
|
+
title: "Empty link href",
|
|
60
|
+
message: "Link href attribute is empty",
|
|
61
|
+
line
|
|
62
|
+
});
|
|
63
|
+
else if (href === "#" || href === "/") issues.push({
|
|
64
|
+
type: "warning",
|
|
65
|
+
title: "Placeholder link",
|
|
66
|
+
message: `Link href is "${href}"`,
|
|
67
|
+
line
|
|
68
|
+
});
|
|
69
|
+
else if (href.startsWith("http:")) issues.push({
|
|
70
|
+
type: "warning",
|
|
71
|
+
title: "Insecure link",
|
|
72
|
+
message: "Link uses HTTP instead of HTTPS",
|
|
73
|
+
line
|
|
74
|
+
});
|
|
75
|
+
else if (href.startsWith("http") && !/^https?:\/\/.+\..+/i.test(href)) issues.push({
|
|
76
|
+
type: "warning",
|
|
77
|
+
title: "Invalid link",
|
|
78
|
+
message: `Link href "${href}" looks malformed`,
|
|
79
|
+
line
|
|
70
80
|
});
|
|
71
|
-
else {
|
|
72
|
-
const href = hrefMatch[1].trim();
|
|
73
|
-
if (!href) issues.push({
|
|
74
|
-
type: "warning",
|
|
75
|
-
title: "Empty link href",
|
|
76
|
-
message: "Link href attribute is empty",
|
|
77
|
-
line: lineNum
|
|
78
|
-
});
|
|
79
|
-
else if (href === "#" || href === "/") issues.push({
|
|
80
|
-
type: "warning",
|
|
81
|
-
title: "Placeholder link",
|
|
82
|
-
message: `Link href is "${href}"`,
|
|
83
|
-
line: lineNum
|
|
84
|
-
});
|
|
85
|
-
else if (href.startsWith("http:")) issues.push({
|
|
86
|
-
type: "warning",
|
|
87
|
-
title: "Insecure link",
|
|
88
|
-
message: "Link uses HTTP instead of HTTPS",
|
|
89
|
-
line: lineNum
|
|
90
|
-
});
|
|
91
|
-
else if (href.startsWith("http") && !/^https?:\/\/.+\..+/i.test(href)) issues.push({
|
|
92
|
-
type: "warning",
|
|
93
|
-
title: "Invalid link",
|
|
94
|
-
message: `Link href "${href}" looks malformed`,
|
|
95
|
-
line: lineNum
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
81
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
82
|
+
if ([
|
|
83
|
+
"link",
|
|
84
|
+
"script",
|
|
85
|
+
"source"
|
|
86
|
+
].includes(tagName)) {
|
|
87
|
+
const attrMatch = tag.match(/\b(?:href|src)\s*=\s*["']([^"']*)["']/i);
|
|
102
88
|
if (attrMatch && attrMatch[1].trim().startsWith("http:")) issues.push({
|
|
103
89
|
type: "warning",
|
|
104
90
|
title: "Insecure resource",
|
|
105
91
|
message: "Resource loads over HTTP instead of HTTPS",
|
|
106
|
-
line
|
|
92
|
+
line
|
|
107
93
|
});
|
|
108
94
|
}
|
|
109
|
-
const urlMatches = [...line.matchAll(/url\s*\(\s*["']?(http:[^"')]+)["']?\s*\)/gi)];
|
|
110
|
-
for (const _match of urlMatches) issues.push({
|
|
111
|
-
type: "warning",
|
|
112
|
-
title: "Insecure CSS url()",
|
|
113
|
-
message: "CSS url() loads over HTTP instead of HTTPS",
|
|
114
|
-
line: lineNum
|
|
115
|
-
});
|
|
116
95
|
}
|
|
96
|
+
for (const m of Array.from(html.matchAll(/url\s*\(\s*["']?(http:[^"')]+)["']?\s*\)/gi))) issues.push({
|
|
97
|
+
type: "warning",
|
|
98
|
+
title: "Insecure CSS url()",
|
|
99
|
+
message: "CSS url() loads over HTTP instead of HTTPS",
|
|
100
|
+
line: lineAt(html, m.index, lineOffset)
|
|
101
|
+
});
|
|
117
102
|
const voidElements = new Set([
|
|
118
103
|
"area",
|
|
119
104
|
"base",
|
|
@@ -174,7 +159,11 @@ function lintHtml(html, lineOffset = 0) {
|
|
|
174
159
|
if (!trackedTags.has(tagName) || voidElements.has(tagName)) continue;
|
|
175
160
|
if (fullMatch.endsWith("/>")) continue;
|
|
176
161
|
if (fullMatch.startsWith("</")) {
|
|
177
|
-
|
|
162
|
+
let lastOpen = -1;
|
|
163
|
+
for (let j = stack.length - 1; j >= 0; j--) if (stack[j].tag === tagName) {
|
|
164
|
+
lastOpen = j;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
178
167
|
if (lastOpen !== -1) stack.splice(lastOpen, 1);
|
|
179
168
|
} else stack.push({
|
|
180
169
|
tag: tagName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linter.mjs","names":[],"sources":["../../src/server/linter.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { glob } from 'tinyglobby'\nimport type { MaizzleConfig } from '../types/index.ts'\n\ninterface LintIssue {\n type: 'error' | 'warning'\n title: string\n message: string\n line?: number\n}\n\nexport async function serveLint(url: string, config: MaizzleConfig, res: any) {\n const templateSlug = url.replace('/__maizzle/lint/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end(JSON.stringify({ error: 'Template not found' }))\n return\n }\n\n try {\n const source = readFileSync(resolve(match), 'utf-8')\n\n // Extract only the <template> block for linting\n const templateMatch = source.match(/<template\\b[^>]*>([\\s\\S]*)<\\/template>/)\n const html = templateMatch ? templateMatch[1] : source\n\n // Calculate the offset of the <template> content within the source file\n const templateOffset = templateMatch\n ? source.slice(0, source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1])).split('\\n').length - 1\n : 0\n\n const issues = lintHtml(html, templateOffset)\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n\nfunction lintHtml(html: string, lineOffset = 0): LintIssue[] {\n const issues: LintIssue[] = []\n const lines = html.split('\\n')\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n const lineNum = i + 1 + lineOffset\n\n // Images missing alt text\n const imgMatches = [...line.matchAll(/<img\\b[^>]*?>/gi)]\n for (const match of imgMatches) {\n const tag = match[0]\n if (!/\\balt\\s*=/i.test(tag)) {\n issues.push({\n type: 'warning',\n title: 'Missing alt text',\n message: 'Image is missing the alt attribute',\n line: lineNum,\n })\n }\n }\n\n // Images with empty or missing src\n for (const match of imgMatches) {\n const tag = match[0]\n const srcMatch = tag.match(/\\bsrc\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!srcMatch) {\n issues.push({\n type: 'error',\n title: 'Missing image src',\n message: 'Image tag has no src attribute',\n line: lineNum,\n })\n } else if (!srcMatch[1].trim()) {\n issues.push({\n type: 'error',\n title: 'Empty image src',\n message: 'Image src attribute is empty',\n line: lineNum,\n })\n } else if (srcMatch[1].trim().startsWith('http:')) {\n issues.push({\n type: 'warning',\n title: 'Insecure image src',\n message: 'Image loads over HTTP instead of HTTPS',\n line: lineNum,\n })\n }\n }\n\n // Links: missing href, empty href, placeholder href\n const linkMatches = [...line.matchAll(/<a\\b[^>]*?>/gi)]\n for (const match of linkMatches) {\n const tag = match[0]\n const hrefMatch = tag.match(/\\bhref\\s*=\\s*[\"']([^\"']*)[\"']/i)\n\n if (!hrefMatch) {\n issues.push({\n type: 'error',\n title: 'Missing link href',\n message: 'Anchor tag has no href attribute',\n line: lineNum,\n })\n } else {\n const href = hrefMatch[1].trim()\n if (!href) {\n issues.push({\n type: 'warning',\n title: 'Empty link href',\n message: 'Link href attribute is empty',\n line: lineNum,\n })\n } else if (href === '#' || href === '/') {\n issues.push({\n type: 'warning',\n title: 'Placeholder link',\n message: `Link href is \"${href}\"`,\n line: lineNum,\n })\n } else if (href.startsWith('http:')) {\n issues.push({\n type: 'warning',\n title: 'Insecure link',\n message: 'Link uses HTTP instead of HTTPS',\n line: lineNum,\n })\n } else if (href.startsWith('http') && !/^https?:\\/\\/.+\\..+/i.test(href)) {\n issues.push({\n type: 'warning',\n title: 'Invalid link',\n message: `Link href \"${href}\" looks malformed`,\n line: lineNum,\n })\n }\n }\n }\n\n // Insecure resources (<link href>, <script src>, <source src>)\n const resourceMatches = [...line.matchAll(/<(?:link|script|source)\\b[^>]*?>/gi)]\n for (const match of resourceMatches) {\n const tag = match[0]\n const attrMatch = tag.match(/\\b(?:href|src)\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (attrMatch && attrMatch[1].trim().startsWith('http:')) {\n issues.push({\n type: 'warning',\n title: 'Insecure resource',\n message: 'Resource loads over HTTP instead of HTTPS',\n line: lineNum,\n })\n }\n }\n\n // Insecure CSS url() references\n const urlMatches = [...line.matchAll(/url\\s*\\(\\s*[\"']?(http:[^\"')]+)[\"']?\\s*\\)/gi)]\n for (const _match of urlMatches) {\n issues.push({\n type: 'warning',\n title: 'Insecure CSS url()',\n message: 'CSS url() loads over HTTP instead of HTTPS',\n line: lineNum,\n })\n }\n }\n\n // Check for unclosed tags (block-level and common inline elements)\n const voidElements = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n ])\n\n const trackedTags = new Set([\n 'a', 'b', 'body', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'head', 'html', 'i', 'li', 'ol', 'p', 'span', 'strong', 'style',\n 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'u', 'ul',\n ])\n\n const stack: Array<{ tag: string, line: number }> = []\n\n // Strip comments and content inside <style>/<script> to avoid false matches\n const stripped = html\n .replace(/<!--[\\s\\S]*?-->/g, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n .replace(/<(style|script)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n\n const strippedLines = stripped.split('\\n')\n\n for (let i = 0; i < strippedLines.length; i++) {\n const line = strippedLines[i]\n const tagRegex = /<\\/?([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*\\/?>/g\n let m\n\n while ((m = tagRegex.exec(line)) !== null) {\n const fullMatch = m[0]\n const tagName = m[1].toLowerCase()\n\n if (!trackedTags.has(tagName) || voidElements.has(tagName)) continue\n if (fullMatch.endsWith('/>')) continue\n\n if (fullMatch.startsWith('</')) {\n // Closing tag\n const lastOpen = stack.findLastIndex(s => s.tag === tagName)\n if (lastOpen !== -1) {\n stack.splice(lastOpen, 1)\n }\n } else {\n // Opening tag\n stack.push({ tag: tagName, line: i + 1 + lineOffset })\n }\n }\n }\n\n for (const unclosed of stack) {\n issues.push({\n type: 'error',\n title: 'Unclosed tag',\n message: `<${unclosed.tag}> tag is not closed`,\n line: unclosed.line,\n })\n }\n\n // Sort: errors first, then warnings, then by line\n issues.sort((a, b) => {\n if (a.type !== b.type) return a.type === 'error' ? -1 : 1\n return (a.line ?? 0) - (b.line ?? 0)\n })\n\n return issues\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"linter.mjs","names":[],"sources":["../../src/server/linter.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\ninterface LintIssue {\n type: 'error' | 'warning'\n title: string\n message: string\n line?: number\n}\n\nexport function serveLint(url: string, res: any) {\n const filePath = url.replace('/__maizzle/lint/', '').replace(/\\?.*$/, '')\n\n try {\n const source = readFileSync(resolve(filePath), 'utf-8')\n\n // Extract only the <template> block for linting\n const templateMatch = source.match(/<template\\b[^>]*>([\\s\\S]*)<\\/template>/)\n const html = templateMatch ? templateMatch[1] : source\n\n // Calculate the offset of the <template> content within the source file\n const templateOffset = templateMatch\n ? source.slice(0, source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1])).split('\\n').length - 1\n : 0\n\n const issues = lintHtml(html, templateOffset)\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n\nfunction lineAt(html: string, offset: number, lineOffset: number): number {\n return html.slice(0, offset).split('\\n').length + lineOffset\n}\n\nfunction lintHtml(html: string, lineOffset = 0): LintIssue[] {\n const issues: LintIssue[] = []\n\n // Match all tags (multiline) — [^>] doesn't cross > so use [\\s\\S] with lazy quantifier\n const tagRe = /<([a-zA-Z][a-zA-Z0-9]*)\\b([\\s\\S]*?)>/g\n\n for (const m of Array.from(html.matchAll(tagRe))) {\n const tag = m[0]\n const tagName = m[1].toLowerCase()\n const line = lineAt(html, m.index!, lineOffset)\n\n // Images\n if (tagName === 'img') {\n if (!/\\balt\\s*=/i.test(tag)) {\n issues.push({ type: 'warning', title: 'Missing alt text', message: 'Image is missing the alt attribute', line })\n }\n\n const srcMatch = tag.match(/\\bsrc\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!srcMatch) {\n issues.push({ type: 'error', title: 'Missing image src', message: 'Image tag has no src attribute', line })\n } else if (!srcMatch[1].trim()) {\n issues.push({ type: 'error', title: 'Empty image src', message: 'Image src attribute is empty', line })\n } else if (srcMatch[1].trim().startsWith('http:')) {\n issues.push({ type: 'warning', title: 'Insecure image src', message: 'Image loads over HTTP instead of HTTPS', line })\n }\n }\n\n // Any tag with href (catches <a>, <Button>, etc.)\n const hrefMatch = tag.match(/\\bhref\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (hrefMatch) {\n const href = hrefMatch[1].trim()\n if (!href) {\n issues.push({ type: 'warning', title: 'Empty link href', message: 'Link href attribute is empty', line })\n } else if (href === '#' || href === '/') {\n issues.push({ type: 'warning', title: 'Placeholder link', message: `Link href is \"${href}\"`, line })\n } else if (href.startsWith('http:')) {\n issues.push({ type: 'warning', title: 'Insecure link', message: 'Link uses HTTP instead of HTTPS', line })\n } else if (href.startsWith('http') && !/^https?:\\/\\/.+\\..+/i.test(href)) {\n issues.push({ type: 'warning', title: 'Invalid link', message: `Link href \"${href}\" looks malformed`, line })\n }\n }\n\n // Insecure resources (<link>, <script>, <source>)\n if (['link', 'script', 'source'].includes(tagName)) {\n const attrMatch = tag.match(/\\b(?:href|src)\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (attrMatch && attrMatch[1].trim().startsWith('http:')) {\n issues.push({ type: 'warning', title: 'Insecure resource', message: 'Resource loads over HTTP instead of HTTPS', line })\n }\n }\n }\n\n // Insecure CSS url() references\n for (const m of Array.from(html.matchAll(/url\\s*\\(\\s*[\"']?(http:[^\"')]+)[\"']?\\s*\\)/gi))) {\n issues.push({ type: 'warning', title: 'Insecure CSS url()', message: 'CSS url() loads over HTTP instead of HTTPS', line: lineAt(html, m.index!, lineOffset) })\n }\n\n // Check for unclosed tags (block-level and common inline elements)\n const voidElements = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n ])\n\n const trackedTags = new Set([\n 'a', 'b', 'body', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'head', 'html', 'i', 'li', 'ol', 'p', 'span', 'strong', 'style',\n 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'u', 'ul',\n ])\n\n const stack: Array<{ tag: string, line: number }> = []\n\n // Strip comments and content inside <style>/<script> to avoid false matches\n const stripped = html\n .replace(/<!--[\\s\\S]*?-->/g, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n .replace(/<(style|script)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n\n const strippedLines = stripped.split('\\n')\n\n for (let i = 0; i < strippedLines.length; i++) {\n const line = strippedLines[i]\n const tagRegex = /<\\/?([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*\\/?>/g\n let m\n\n while ((m = tagRegex.exec(line)) !== null) {\n const fullMatch = m[0]\n const tagName = m[1].toLowerCase()\n\n if (!trackedTags.has(tagName) || voidElements.has(tagName)) continue\n if (fullMatch.endsWith('/>')) continue\n\n if (fullMatch.startsWith('</')) {\n // Closing tag\n let lastOpen = -1\n for (let j = stack.length - 1; j >= 0; j--) {\n if (stack[j].tag === tagName) { lastOpen = j; break }\n }\n if (lastOpen !== -1) {\n stack.splice(lastOpen, 1)\n }\n } else {\n // Opening tag\n stack.push({ tag: tagName, line: i + 1 + lineOffset })\n }\n }\n }\n\n for (const unclosed of stack) {\n issues.push({\n type: 'error',\n title: 'Unclosed tag',\n message: `<${unclosed.tag}> tag is not closed`,\n line: unclosed.line,\n })\n }\n\n // Sort: errors first, then warnings, then by line\n issues.sort((a, b) => {\n if (a.type !== b.type) return a.type === 'error' ? -1 : 1\n return (a.line ?? 0) - (b.line ?? 0)\n })\n\n return issues\n}\n"],"mappings":"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"}
|