fix to use with sveltekit

This commit is contained in:
Thiago Lagden
2023-12-11 13:34:34 -03:00
commit e14f35aff0
29 changed files with 4880 additions and 0 deletions

17
.editorconfig Normal file
View File

@@ -0,0 +1,17 @@
root = true
[*]
indent_style = tab
indent_size = 2
tab_width = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{md,yml,yaml,json}]
indent_style = space
indent_size = 2
[*.{md,yml,yaml}]
trim_trailing_whitespace = false

9
.eslintignore Normal file
View File

@@ -0,0 +1,9 @@
.DS_Store
.svelte-kit
build
coverage
dist
node_modules
package
.env*
vite.config.js.timestamp-*

65
.eslintrc.yaml Normal file
View File

@@ -0,0 +1,65 @@
root: true
env:
es2022: true
browser: true
node: true
parserOptions:
ecmaVersion: 13
sourceType: module
extraFileExtensions: ['.svelte']
extends:
- eslint:recommended
- plugin:svelte/recommended
- prettier
# - plugin:unicorn/recommended
rules:
camelcase: off
capitalized-comments: off
indent: [error, tab]
linebreak-style: [error, unix]
no-console: off
no-debugger: off
# no-multi-assign: off
# no-multiple-empty-lines:
# - error
# -
# max: 2
# maxBOF: 2
# maxEOF: 0
# no-new-func: off
no-undef-init: off
no-unused-expressions:
- error
- allowShortCircuit: true
allowTernary: true
allowTaggedTemplates: true
padding-line-between-statements: off
quotes: [error, single]
semi: [error, never]
semi-spacing:
- error
- before: false
after: true
spaced-comment: off
svelte/no-at-html-tags: off
# unicorn/consistent-destructuring: off
unicorn/filename-case: off
# unicorn/import-style:
# - error
# -
# styles:
# util: false
# path:
# named: true
# unicorn/no-abusive-eslint-disable: off
unicorn/no-array-reduce: off
# unicorn/no-negated-condition: off
# unicorn/no-null: off
unicorn/no-useless-undefined: off
unicorn/no-zero-fractions: off
# unicorn/prefer-dom-node-dataset: off
unicorn/prefer-includes: off
# unicorn/prefer-object-from-entries: off
unicorn/prefer-query-selector: off
unicorn/prevent-abbreviations: off
unicorn/prefer-top-level-await: off

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
.DS_Store
.svelte-kit
build
coverage
dist
node_modules
package
.env*
vite.config.js.timestamp-*
coverage/**
!coverage/lcov.info

33
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,33 @@
include:
- project: "inovacao/devops/template-cicd"
file: "test-package-js.yaml"
- project: "inovacao/devops/template-cicd"
file: "sonar-js.yaml"
- project: "inovacao/devops/template-cicd"
file: "publish-package-js.yaml"
- project: "inovacao/devops/template-cicd"
file: "copy-package-js.yaml"
test:
extends: .test
sonar:
extends: .sonar_js
variables:
SONAR_SOURCE: "src"
SONAR_TEST: "tests"
publish:
extends: .publish
script:
- npm i
- !reference [.publish, script]
copy:
extends: .copy
stages:
- test
- sonar
- release
- copy

3
.npmrc Normal file
View File

@@ -0,0 +1,3 @@
engine-strict=true
resolution-mode=highest
auto-install-peers=true

15
.prettierignore Normal file
View File

@@ -0,0 +1,15 @@
*.css
.*
bin
build
coverage
dist
node_modules
package
# template.js
vite.config.js.timestamp-*
*.json
*.yaml
*.yml
*.md

23
.prettierrc.yaml Normal file
View File

@@ -0,0 +1,23 @@
arrowParens: avoid
bracketSameLine: false
bracketSpacing: false
# embeddedLanguageFormatting: auto
insertPragma: false
htmlWhitespaceSensitivity: css
printWidth: 120
proseWrap: preserve
quoteProps: as-needed
requirePragma: false
semi: false
singleAttributePerLine: false
singleQuote: true
trailingComma: all
tabWidth: 4
useTabs: true
overrides:
- files: '*.svelte'
options:
parser: svelte
plugins:
- prettier-plugin-svelte

71
README.md Normal file
View File

@@ -0,0 +1,71 @@
# svelte-editor-quill
[![NPM version][npm-img]][npm]
[![Build Status][ci-img]][ci]
[npm-img]: https://img.shields.io/npm/v/@tadashi/svelte-editor-quill.svg
[npm]: https://www.npmjs.com/package/@tadashi/svelte-editor-quill
[ci-img]: https://github.com/lagden/svelte-editor-quill/workflows/Node.js%20CI/badge.svg
[ci]: https://github.com/lagden/svelte-editor-quill/actions?query=workflow%3A%22Node.js+CI%22
---
Svelte component
## Install
```
$ npm i @tadashi/svelte-editor-quill
```
## Usage
See an example here: https://svelte.dev/repl/839ad6a3e1e24b149099c704e18df476?version=3.32.3
### options
> Type: object
> Default: {theme: 'snow'}
See the `options` here: https://quilljs.com/docs/configuration/#options
### options.plainclipboard
> Type: boolean
> Default: false
Accept only paste plain text.
### Sample
```html
<script>
import {Editor} from '@tadashi/svelte-editor-quill'
const options = {
theme: 'snow',
plainclipboard: true
}
function onTextChange(event) {
console.log(event.detail)
}
</script>
<svelte:head>
<link rel="preconnect" href="https://cdn.quilljs.com" crossorigin>
<link rel="stylesheet" href="https://cdn.quilljs.com/1.3.7/quill.snow.css">
</svelte:head>
<Editor {options} on:text-change={onTextChange} data='Apenas um show' />
```
## License
MIT © [Thiago Lagden](https://github.com/lagden)

54
bin/helper/fn Executable file
View File

@@ -0,0 +1,54 @@
#!/bin/sh
abort() {
printf "\n \033[31mError: $@\033[0m\n\n" && exit 1
}
ok() {
printf "\n \033[32mOk: $@\033[0m\n\n"
}
load_env() {
_DIR="$(pwd)"
ENVFILE_LOCAL="${2:-$_DIR}/.conf/local.sh"
ENVFILE_OPT="${2:-$_DIR}/.conf/${1:-development}.sh"
USE_LOCAL_ENV=0
if test ! -e $ENVFILE_OPT; then
abort "Environment file not found"
fi
set -a
. ${ENVFILE_OPT}
set +a
if test "${1}" = "development" -o "${1}" = "test"; then
USE_LOCAL_ENV=1
fi
if test -e $ENVFILE_LOCAL -a "${USE_LOCAL_ENV:-0}" = "1"; then
set -a
. ${ENVFILE_LOCAL}
set +a
fi
}
gen_env() {
_DIR="$(cd $(dirname $0) && pwd)"
_BIN_DIR="$(cd $DIR/.. && pwd)"
GEN_ENV="${2:-$_BIN_DIR}/node/env.js"
if test -z $1; then
abort "Missing output"
fi
if test ! -f "${GEN_ENV}"; then
abort "File not found: ${GEN_ENV}"
fi
_DIR_FILE=$(dirname $1)
mkdir -p $_DIR_FILE
$GEN_ENV > $1
ok "ENVs generated... ${1}"
}

32
bin/helper/wait Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
: ${SLEEP_LENGTH:=2}
: ${TIMEOUT_LENGTH:=60}
DIR="$(cd $(dirname $0) && pwd)"
BIN_DIR="$(cd $DIR/.. && pwd)"
# Import functions
. $BIN_DIR/helper/fn
wait_for() {
START="$(date +%s)"
echo "Waiting for $1 to listen on $2..."
while ! nc -z $1 $2; do
if [ $(($(date +%s) - $START)) -gt $TIMEOUT_LENGTH ]; then
abort "Service $1:$2 did not start within $TIMEOUT_LENGTH seconds. Aborting..."
fi
echo "sleeping"
sleep $SLEEP_LENGTH
done
}
for var in "$@"
do
host=${var%:*}
port=${var#*:}
wait_for $host $port
ok "Listening ${host}:${port}"
done
exit 0

68
bin/node/pkg.js Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env node
import path from 'node:path'
import {pathToFileURL} from 'node:url'
import {createWriteStream} from 'node:fs'
import {readFile} from 'node:fs/promises'
import {promisify} from 'node:util'
import child_process from 'node:child_process'
const exec = promisify(child_process.exec)
const packageFile = pathToFileURL(path.resolve(process.cwd(), 'package.json'))
const packageBuf = await readFile(packageFile)
const packageJson = JSON.parse(packageBuf)
const {
dependencies,
devDependencies,
} = packageJson
let cc = 0
function _error(message) {
process.stderr.write(message)
process.exit(1)
}
function getLatestVersionPackage(data, prop) {
if (!data) {
return Promise.resolve('no data to show')
}
const keys = Object.keys(data)
return Promise.allSettled(
keys.map(async name => {
const cmd = `npm show ${name} version`
let {stdout: version} = await exec(cmd)
version = String(version).replace('\n', '')
if (version && data[name] !== String(version)) {
cc++
process.stdout.write(`${name} --> ${version}\n`)
packageJson[prop][name] = version
return {name, version}
}
}),
)
}
try {
await Promise.all([
getLatestVersionPackage(dependencies, 'dependencies'),
getLatestVersionPackage(devDependencies, 'devDependencies'),
])
createWriteStream(packageFile)
.on('finish', () => {
process.stdout.write(cc > 0 ? 'All writes are now complete.' : 'No updates')
})
.on('close', () => {
process.exit(0)
})
.on('error', error => {
_error(error.message)
})
.end(`${JSON.stringify(packageJson, undefined, ' ')}\n`)
} catch (error) {
_error(error.message)
}

94
bin/node/zera Executable file
View File

@@ -0,0 +1,94 @@
#!/bin/bash
DIR="$(cd $(dirname $0) && pwd)"
BIN_DIR="$(cd $DIR/.. && pwd)"
# Import functions
. $BIN_DIR/helper/fn
# Usage
usage() {
cat <<-EOF
Usage: $0 [options...]
Options:
-m, --manager <manager> Package manager: npm (default), yarn or pnpm
-s, --shame Shamefully hoist (only pnpm)
-h, --help Show usage
EOF
exit 2
}
unset CURR_FOLDER
CURR_FOLDER="$(pwd)"
unset ACTION
ACTION=npm
unset SHAME
SHAME=""
unset VERBOSE
VERBOSE=false
unset CMD_AVAILABLE
CMD_AVAILABLE=("npm" "yarn" "pnpm")
for i in "$@"; do
case $i in
-h|--help)
usage
;;
-m|--manager)
ACTION="${2}"
shift
;;
-s|--shame)
SHAME="--shamefully-hoist"
shift
;;
-*|--*)
abort "Unknown option $i"
;;
*)
;;
esac
done
# Check command available
[[ ! " ${CMD_AVAILABLE[*]} " =~ " ${ACTION} " ]] && usage
# Go to current directory
cd $CURR_FOLDER
# Remove current data
rm -rf \
"${CURR_FOLDER}/node_modules" \
"${CURR_FOLDER}/package-lock.json" \
"${CURR_FOLDER}/yarn.lock" \
"${CURR_FOLDER}/.yarn" \
"${CURR_FOLDER}/.yarnrc.yml" \
"${CURR_FOLDER}/.pnp.*" \
"${CURR_FOLDER}/pnpm-lock.yaml"
# Generate .yarnrc.yml
if [ "${ACTION}" = "yarn" ]; then
echo "" > "${CURR_FOLDER}/.yarnrc.yml"
yarn set version stable
echo "nodeLinker: node-modules" >> "${CURR_FOLDER}/.yarnrc.yml"
fi
# Install packages
if [ "${ACTION}" = "pnpm" ]; then
$ACTION install $SHAME
else
$ACTION install
fi
# Audit packages
test $? -ne 0 && abort "${ACTION} failed" || ok "zerado..."
test "${ACTION}" = "yarn" && yarn npm audit || $ACTION audit
exit 0

12
jsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"moduleResolution": "NodeNext",
"module": "NodeNext"
}
}

87
package.json Normal file
View File

@@ -0,0 +1,87 @@
{
"name": "@tadashi/svelte-editor-quill",
"version": "2.2.0",
"description": "Svelte component",
"author": "Thiago Lagden",
"license": "MIT",
"bugs": {
"url": "https://github.com/lagden/svelte-editor-quill/issues"
},
"homepage": "https://github.com/lagden/svelte-editor-quill#readme",
"engines": {
"node": ">=12.0.0"
},
"keywords": [
"svelte",
"editor",
"quill",
"component"
],
"svelte": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
},
"./dist/quill.js": {
"types": "./dist/quill.d.ts",
"svelte": "./dist/quill.js"
},
"./dist/Editor.svelte": {
"types": "./dist/Editor.svelte.d.ts",
"svelte": "./dist/Editor.svelte"
}
},
"files": [
"dist",
"!dist/**/*.test.*",
"!dist/**/*.spec.*"
],
"scripts": {
"rm": "rimraf dist",
"dev": "vite dev",
"build:vite": "vite build",
"build": "run-s rm build:vite package",
"preview": "vite preview",
"package": "svelte-kit sync && svelte-package && publint",
"prepublishOnly": "run-s test build",
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
"test": "run-s rm lint test:integration test:unit",
"lint": "prettier --check --plugin prettier-plugin-svelte . && eslint .",
"format": "prettier --write --plugin prettier-plugin-svelte .",
"test:integration": "playwright test",
"test:unit": "vitest --run --coverage",
"test:ui": "vitest --ui --coverage"
},
"peerDependencies": {
"svelte": ">=3.50.0"
},
"dependencies": {
"quill": "1.3.7"
},
"devDependencies": {
"@playwright/test": "1.40.1",
"@sveltejs/adapter-auto": "2.1.1",
"@sveltejs/kit": "1.28.0",
"@sveltejs/package": "2.2.3",
"@vitest/coverage-v8": "1.0.4",
"@vitest/ui": "1.0.4",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-svelte": "2.35.1",
"jsdom": "23.0.1",
"npm-run-all": "4.1.5",
"prettier": "3.1.1",
"prettier-plugin-svelte": "3.1.2",
"publint": "0.2.6",
"rimraf": "5.0.5",
"svelte": "4.2.8",
"svelte-check": "3.6.2",
"tslib": "2.6.2",
"typescript": "5.3.3",
"vite": "4.5.1",
"vitest": "1.0.4"
}
}

12
playwright.config.js Normal file
View File

@@ -0,0 +1,12 @@
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
webServer: {
command: 'env PREVIEW=1 npm run build && npm run preview',
port: 4173,
},
testDir: 'tests/e2e',
testMatch: /(.+\.)(test|spec)\.[jt]s/,
outputDir: 'tests/e2e/__snapshots__',
}
export default config

4043
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

14
src/app.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
/** @format */
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {}

14
src/app.html Normal file
View File

@@ -0,0 +1,14 @@
<!-- @format -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div>%sveltekit.body%</div>
</body>
</html>

30
src/lib/Editor.svelte Normal file
View File

@@ -0,0 +1,30 @@
<script>
import {onMount, onDestroy} from 'svelte'
export let options = {}
export let data = ''
let className = ''
export {className as class}
options = {
theme: 'snow',
...options,
}
let node
let destroy
onMount(async () => {
const {quill} = await import('./quill.js')
destroy = quill(node, options)
})
onDestroy(() => {
destroy && destroy()
})
</script>
<div class={className}>
<div bind:this={node} on:text-change>{@html data}</div>
</div>

2
src/lib/index.js Normal file
View File

@@ -0,0 +1,2 @@
// prettier-ignore
export {default as Editor} from './Editor.svelte'

61
src/lib/quill.js Normal file
View File

@@ -0,0 +1,61 @@
import Quill from 'quill'
const Clipboard = Quill.import('modules/clipboard')
const Delta = Quill.import('delta')
class PlainClipboard extends Clipboard {
convert(html) {
if (typeof html === 'string') {
this.container.innerHTML = html
}
const text = this.container.textContent
this.container.innerHTML = ''
return new Delta().insert(text)
}
}
export function quill(node, options) {
const toolbar = [
[{header: 1}, {header: 2}],
['bold', 'italic', 'underline'],
[{list: 'ordered'}, {list: 'bullet'}],
['link', 'image', 'video'],
['code-block', 'clean'],
]
const {plainclipboard} = options
if (plainclipboard) {
Quill.register('modules/clipboard', PlainClipboard, true)
}
if (Reflect.has(options, 'plainclipboard')) {
Reflect.deleteProperty(options, 'plainclipboard')
}
const q = new Quill(node, {
modules: {
toolbar,
},
placeholder: 'Digite algo...',
...options,
})
const _container = node.querySelector('.ql-editor')
const onTextChange = () => {
const html = _container?.innerHTML ?? ''
const customEvent = new CustomEvent('text-change', {
detail: {
html,
text: q.getText(),
},
})
node.dispatchEvent(customEvent)
}
q.on('text-change', onTextChange)
return () => {
q.off('text-change', onTextChange)
}
}

19
src/routes/+page.svelte Normal file
View File

@@ -0,0 +1,19 @@
<script>
import {Editor} from '$lib/index.js'
const options = {
theme: 'snow',
plainclipboard: true,
}
function onTextChange(event) {
console.debug(event.detail)
}
</script>
<svelte:head>
<link rel="preconnect" href="https://cdn.quilljs.com" crossorigin />
<link rel="stylesheet" href="https://cdn.quilljs.com/1.3.7/quill.snow.css" crossorigin />
</svelte:head>
<Editor {options} on:text-change={onTextChange} data="Apenas um show" />

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

10
svelte.config.js Normal file
View File

@@ -0,0 +1,10 @@
import adapter from '@sveltejs/adapter-auto'
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter(),
},
}
export default config

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

15
tests/e2e/index.spec.js Normal file
View File

@@ -0,0 +1,15 @@
import {setTimeout} from 'node:timers/promises'
import {
// expect,
test,
} from '@playwright/test'
test('general', async ({page}) => {
await page.goto('/')
await setTimeout(1000)
await page.screenshot({
path: './tests/e2e/__snapshots__/index.spec.js.png',
})
})

26
tests/unit/quill.spec.js Normal file
View File

@@ -0,0 +1,26 @@
import {setTimeout} from 'timers/promises'
import {test, expect, beforeAll} from 'vitest'
import {Editor} from '$lib/index.js'
beforeAll(() => {
document.body.innerHTML = '<main id="xxx"></main>'
})
function doc_query(selector) {
const node = document.querySelector(selector)
if (!node) {
throw new Error(`No element found for selector: ${selector}`)
}
return node
}
test('Editor', async () => {
const target = doc_query('main#xxx')
// const component = new Editor({target})
new Editor({target})
await setTimeout(3000)
// const div = doc_query('div.ql-editor')
expect(target).toBeDefined()
})

39
vite.config.js Normal file
View File

@@ -0,0 +1,39 @@
import {sveltekit} from '@sveltejs/kit/vite'
import {defineConfig} from 'vitest/config'
const preview = process?.env?.PREVIEW ?? false
const config = {
plugins: [sveltekit()],
test: {
include: ['tests/unit/**/*.{test,spec}.{js,ts}'],
environment: 'jsdom',
threads: false,
globals: true,
coverage: {
// prettier-ignore
include: ['src/lib/**'],
// prettier-ignore
reporter: [
'text',
'text-summary',
'lcovonly',
'cobertura'
],
},
},
build: {
sourcemap: true,
rollupOptions: {
// prettier-ignore
external: [
'quill',
],
},
},
}
if (preview) {
Reflect.deleteProperty(config, 'build')
}
export default defineConfig(config)