From 04f9219c305ea45e2770c28672b16e7d242d4a9c Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Thu, 14 Mar 2024 15:49:59 +0100 Subject: [PATCH 01/12] chore: update tooling tsconfig + package.json metas + eslint/prettier --- Dockerfile | 21 ---- docker-compose.yml => compose.yml | 2 +- docker-compose-test.yml | 12 --- package.json | 158 ++++++++++++------------------ start.sh | 3 - tsconfig.json | 12 +-- 6 files changed, 68 insertions(+), 140 deletions(-) delete mode 100644 Dockerfile rename docker-compose.yml => compose.yml (97%) delete mode 100644 docker-compose-test.yml delete mode 100644 start.sh diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index de07968..0000000 --- a/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM node:14.16-alpine as build-deps - -RUN apk update && apk upgrade && \ - apk add --update git && \ - apk add --update openssh && \ - apk add --update bash && \ - apk add --update wget && \ - apk add --update g++ make python3 - -WORKDIR /usr/src/app - -COPY package*.json ./ -RUN HUSKY_SKIP_INSTALL=1 npm install --build-from-source --python=/usr/bin/python3 - -RUN wget https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh -O /wait-for-it.sh -RUN chmod +x /wait-for-it.sh - -COPY . . -COPY ./start.sh /start.sh -RUN chmod +x /start.sh -RUN npm run build diff --git a/docker-compose.yml b/compose.yml similarity index 97% rename from docker-compose.yml rename to compose.yml index a1af789..5876fd0 100644 --- a/docker-compose.yml +++ b/compose.yml @@ -23,7 +23,7 @@ services: ports: - '3307:3306' pg: - image: postgres:11 + image: postgres:15 restart: always environment: POSTGRES_DB: lucid_slugify diff --git a/docker-compose-test.yml b/docker-compose-test.yml deleted file mode 100644 index e6318d4..0000000 --- a/docker-compose-test.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3.4' -services: - test: - build: - context: . - target: build-deps - links: - - mysql_legacy - - mysql - - pg - - mssql - command: ['/start.sh'] diff --git a/package.json b/package.json index 5d58bca..5af8ef2 100644 --- a/package.json +++ b/package.json @@ -1,54 +1,44 @@ { "name": "@adonisjs/lucid-slugify", - "version": "2.2.1", "description": "Generate unique slugs for your Lucid models", - "main": "build/providers/SlugifyProvider.js", + "version": "2.2.1", + "engines": { + "node": ">=20.6.0" + }, + "type": "module", "files": [ - "build/adonis-typings", - "build/providers", - "build/src" - ], - "typings": "./build/adonis-typings/index.d.ts", - "keywords": [ - "adonisjs", - "adonis-lucid" + "build" ], "scripts": { - "mrm": "mrm --preset=@adonisjs/mrm-preset", + "clean": "del-cli build", + "copy:templates": "copyfiles --up 1 \"stubs/**/*.stub\" build", + "typecheck": "tsc --noEmit", + "lint": "eslint . --ext=.ts", + "format": "prettier --write .", + "quick:test": "DB=sqlite node --enable-source-maps --loader=ts-node/esm ./bin/test.js", "pretest": "npm run lint", - "test:sqlite": "DB=sqlite FORCE_COLOR=true node ./.bin/test.js", - "test:mysql": "DB=mysql FORCE_COLOR=true node ./.bin/test.js", - "test:mysql_legacy": "DB=mysql_legacy FORCE_COLOR=true node ./.bin/test.js", - "test:mssql": "DB=mssql FORCE_COLOR=true node ./.bin/test.js", - "test:pg": "DB=pg FORCE_COLOR=true node ./.bin/test.js", + "prebuild": "npm run lint && npm run clean", + "compile": "tsup-node && tsc --emitDeclarationOnly --declaration", + "build": "tsup-node && tsc --emitDeclarationOnly --declaration && npm run copy:templates", + "release": "np", + "test:sqlite": "DB=sqlite FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", + "test:mysql": "DB=mysql FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", + "test:mysql_legacy": "DB=mysql_legacy FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", + "test:mssql": "DB=mssql FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", + "test:pg": "DB=pg FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", "test:docker": "npm run test:mysql && npm run test:mysql_legacy && npm run test:pg && npm run test:mssql", "test": "docker-compose -f docker-compose.yml -f docker-compose-test.yml build && docker-compose -f docker-compose.yml -f docker-compose-test.yml run --rm test && npm run test:sqlite", - "clean": "del build", - "compile": "npm run lint && npm run clean && tsc", - "build": "npm run compile", - "prepublishOnly": "npm run build", - "lint": "eslint . --ext=.ts", - "format": "prettier --write .", - "release": "np --message=\"chore(release): %s\"", "version": "npm run build", - "sync-labels": "github-label-sync --labels ./node_modules/@adonisjs/mrm-preset/gh-labels.json adonisjs/lucid-slugify", - "commit": "git-cz" + "prepublishOnly": "npm run build" }, - "author": "virk,adonisjs", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/adonisjs/lucid-slugify.git" - }, - "bugs": { - "url": "https://github.com/adonisjs/lucid-slugify/issues" - }, - "homepage": "https://github.com/adonisjs/lucid-slugify#readme", "devDependencies": { "@adonisjs/core": "^5.8.2", + "@adonisjs/eslint-config": "^1.3.0", "@adonisjs/lucid": "^18.0.0", "@adonisjs/mrm-preset": "^5.0.3", + "@adonisjs/prettier-config": "^1.3.0", "@adonisjs/require-ts": "^2.0.11", + "@adonisjs/tsconfig": "^1.3.0", "@poppinss/dev-utils": "^2.0.3", "@types/node": "^18.7.3", "commitizen": "^4.2.4", @@ -72,77 +62,57 @@ "tedious": "^15.0.1", "typescript": "^4.6.4" }, - "peerDependencies": { - "@adonisjs/core": "^5.8.0", - "@adonisjs/lucid": "^18.0.0" - }, - "np": { - "contents": ".", - "anyBranch": false - }, - "publishConfig": { - "tag": "latest", - "access": "public" - }, - "adonisjs": { - "types": "@adonisjs/lucid-slugify", - "providers": [ - "@adonisjs/lucid-slugify" - ] - }, "dependencies": { "@poppinss/utils": "^4.0.4", "nanoid": "^3.3.4" }, - "directories": { - "test": "test" + "peerDependencies": { + "@adonisjs/core": "^6.2.0", + "@adonisjs/lucid": "^20.0.0" }, - "mrmConfig": { - "core": true, - "license": "MIT", - "services": [ - "github-actions" - ], - "minNodeVersion": "14.15.4", - "probotApps": [ - "stale", - "lock" - ], - "runGhActionsOnWindows": false + "author": "virk,adonisjs", + "license": "MIT", + "homepage": "https://github.com/adonisjs/lucid-slugify#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/adonisjs/lucid-slugify.git" }, - "eslintConfig": { - "extends": [ - "plugin:adonis/typescriptPackage", - "prettier" - ], - "plugins": [ - "prettier" - ], - "rules": { - "prettier/prettier": [ - "error", - { - "endOfLine": "auto" - } - ] - } + "bugs": { + "url": "https://github.com/adonisjs/lucid-slugify/issues" }, - "eslintIgnore": [ - "build" + "keywords": [ + "adonisjs", + "adonis-lucid", + "slugify", + "slug" ], - "prettier": { - "trailingComma": "es5", - "semi": false, - "singleQuote": true, - "useTabs": false, - "quoteProps": "consistent", - "bracketSpacing": true, - "arrowParens": "always", - "printWidth": 100 + "eslintConfig": { + "extends": "@adonisjs/eslint-config/package" + }, + "prettier": "@adonisjs/prettier-config", + "publishConfig": { + "access": "public", + "tag": "next" + }, + "np": { + "message": "chore(release): %s", + "tag": "next", + "branch": "next", + "anyBranch": false }, "config": { "commitizen": { "path": "cz-conventional-changelog" } + }, + "c8": { + "reporter": [ + "text", + "html" + ], + "exclude": [ + "tests/**", + "tests_helpers/**" + ] } } diff --git a/start.sh b/start.sh deleted file mode 100644 index f5a70e3..0000000 --- a/start.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -/wait-for-it.sh mysql:3306 && /wait-for-it.sh mysql_legacy:3306 && /wait-for-it.sh pg:5432 && /wait-for-it.sh mssql:1433 && npm run test:docker diff --git a/tsconfig.json b/tsconfig.json index af84625..ad0cc44 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,7 @@ { - "extends": "./node_modules/@adonisjs/mrm-preset/_tsconfig", + "extends": "@adonisjs/tsconfig/tsconfig.package.json", "compilerOptions": { - "types": [ - "@adonisjs/core", - "@adonisjs/lucid", - "@types/node" - ], - "skipLibCheck": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true + "rootDir": "./", + "outDir": "./build" } } From 50e5da00476893520b2c2c76e5ceb51652335875 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Thu, 14 Mar 2024 15:52:57 +0100 Subject: [PATCH 02/12] chore: update dependencies --- .husky/commit-msg | 4 +--- .npmrc | 1 + package.json | 42 ++++++++++++++++-------------------------- 3 files changed, 18 insertions(+), 29 deletions(-) create mode 100644 .npmrc diff --git a/.husky/commit-msg b/.husky/commit-msg index 4654c12..72e16c6 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,3 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" -HUSKY_GIT_PARAMS=$1 node ./node_modules/@adonisjs/mrm-preset/validate-commit/conventional/validate.js +npx --no -- commitlint --edit diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/package.json b/package.json index 5af8ef2..caeed08 100644 --- a/package.json +++ b/package.json @@ -32,39 +32,29 @@ "prepublishOnly": "npm run build" }, "devDependencies": { - "@adonisjs/core": "^5.8.2", + "@adonisjs/core": "^6.3.1", "@adonisjs/eslint-config": "^1.3.0", - "@adonisjs/lucid": "^18.0.0", + "@adonisjs/lucid": "^20.4.0", "@adonisjs/mrm-preset": "^5.0.3", "@adonisjs/prettier-config": "^1.3.0", - "@adonisjs/require-ts": "^2.0.11", "@adonisjs/tsconfig": "^1.3.0", - "@poppinss/dev-utils": "^2.0.3", - "@types/node": "^18.7.3", - "commitizen": "^4.2.4", - "cz-conventional-changelog": "^3.3.0", - "del-cli": "^5.0.0", - "dotenv": "^16.0.1", - "eslint": "^8.16.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-adonis": "^2.1.0", - "eslint-plugin-prettier": "^4.0.0", - "github-label-sync": "^2.2.0", - "husky": "^8.0.1", - "japa": "^4.0.0", - "mrm": "^4.0.0", + "@types/node": "^20.11.27", + "del-cli": "^5.1.0", + "dotenv": "^16.4.5", + "eslint": "^8.57.0", + "husky": "^9.0.11", "mysql": "^2.18.1", - "np": "^7.6.1", - "pg": "^8.7.3", - "prettier": "^2.6.2", - "reflect-metadata": "^0.1.13", - "sqlite3": "^5.0.8", - "tedious": "^15.0.1", - "typescript": "^4.6.4" + "np": "^10.0.1", + "pg": "^8.11.3", + "prettier": "^3.2.5", + "reflect-metadata": "^0.2.1", + "sqlite3": "^5.1.7", + "tedious": "^18.1.0", + "typescript": "~5.4.2" }, "dependencies": { - "@poppinss/utils": "^4.0.4", - "nanoid": "^3.3.4" + "@poppinss/utils": "^6.7.2", + "nanoid": "^5.0.6" }, "peerDependencies": { "@adonisjs/core": "^6.2.0", From 4425b2e60199a936103bc4270e11aec96efb8700 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Thu, 14 Mar 2024 15:53:44 +0100 Subject: [PATCH 03/12] chore: add missing dev deps --- package.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/package.json b/package.json index caeed08..3e97b30 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,12 @@ "@adonisjs/mrm-preset": "^5.0.3", "@adonisjs/prettier-config": "^1.3.0", "@adonisjs/tsconfig": "^1.3.0", + "@japa/assert": "^2.1.0", + "@japa/file-system": "^2.2.0", + "@japa/runner": "^3.1.1", + "@swc/core": "^1.4.8", "@types/node": "^20.11.27", + "copyfiles": "^2.4.1", "del-cli": "^5.1.0", "dotenv": "^16.4.5", "eslint": "^8.57.0", @@ -50,6 +55,8 @@ "reflect-metadata": "^0.2.1", "sqlite3": "^5.1.7", "tedious": "^18.1.0", + "ts-node": "^10.9.2", + "tsup": "^8.0.2", "typescript": "~5.4.2" }, "dependencies": { From 055a856640b917677cbbf6992ca14964ff903426 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Fri, 15 Mar 2024 15:22:57 +0100 Subject: [PATCH 04/12] refactor: move to v6 --- .bin/test.js | 7 - adonis-typings/container.ts | 16 -- adonis-typings/slugify.ts | 89 ------- bin/test.ts | 21 ++ configure.ts | 21 ++ adonis-typings/index.ts => index.ts | 5 +- package.json | 24 +- providers/SlugifyProvider.ts | 29 --- providers/slugify_provider.ts | 42 ++++ services/main.ts | 19 ++ services/slugify.ts | 19 ++ src/SlugifyManager/index.ts | 96 -------- .../Slugify.ts => decorators/slugify.ts} | 22 +- src/{Slugifier/index.ts => slugifier.ts} | 19 +- src/slugify_manager.ts | 65 ++++++ .../db_increment.ts} | 26 +-- .../ShortId.ts => strategies/short_id.ts} | 14 +- .../Simple.ts => strategies/simple.ts} | 23 +- src/types.ts | 55 +++++ tests/configure.spec.ts | 58 +++++ .../db_increment_strategy.spec.ts | 166 ++++++------- test-helpers/index.ts => tests/helpers.ts | 104 ++++----- .../short_id_strategy.spec.ts | 37 +-- .../simple_strategy.spec.ts | 54 ++--- {test => tests}/slugifier.spec.ts | 110 ++++----- .../slugify_decorator.spec.ts | 219 ++++++++++-------- 26 files changed, 691 insertions(+), 669 deletions(-) delete mode 100644 .bin/test.js delete mode 100644 adonis-typings/container.ts delete mode 100644 adonis-typings/slugify.ts create mode 100644 bin/test.ts create mode 100644 configure.ts rename adonis-typings/index.ts => index.ts (58%) delete mode 100644 providers/SlugifyProvider.ts create mode 100644 providers/slugify_provider.ts create mode 100644 services/main.ts create mode 100644 services/slugify.ts delete mode 100644 src/SlugifyManager/index.ts rename src/{Decorators/Slugify.ts => decorators/slugify.ts} (79%) rename src/{Slugifier/index.ts => slugifier.ts} (78%) create mode 100644 src/slugify_manager.ts rename src/{Strategies/DbIncrement.ts => strategies/db_increment.ts} (90%) rename src/{Strategies/ShortId.ts => strategies/short_id.ts} (56%) rename src/{Strategies/Simple.ts => strategies/simple.ts} (53%) create mode 100644 src/types.ts create mode 100644 tests/configure.spec.ts rename test/db-increment-strategy.spec.ts => tests/db_increment_strategy.spec.ts (58%) rename test-helpers/index.ts => tests/helpers.ts (63%) rename test/short-id-strategy.spec.ts => tests/short_id_strategy.spec.ts (60%) rename test/simple-strategy.spec.ts => tests/simple_strategy.spec.ts (60%) rename {test => tests}/slugifier.spec.ts (57%) rename test/slugify-decorator.spec.ts => tests/slugify_decorator.spec.ts (55%) diff --git a/.bin/test.js b/.bin/test.js deleted file mode 100644 index e20c2dd..0000000 --- a/.bin/test.js +++ /dev/null @@ -1,7 +0,0 @@ -require('@adonisjs/require-ts/build/register') - -const { configure } = require('japa') - -configure({ - files: ['test/**/*.spec.ts'], -}) diff --git a/adonis-typings/container.ts b/adonis-typings/container.ts deleted file mode 100644 index 5e833b9..0000000 --- a/adonis-typings/container.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * @adonisjs/lucid-slugify - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare module '@ioc:Adonis/Core/Application' { - import LucidSlugify from '@ioc:Adonis/Addons/LucidSlugify' - - interface ContainerBindings { - 'Adonis/Addons/LucidSlugify': typeof LucidSlugify - } -} diff --git a/adonis-typings/slugify.ts b/adonis-typings/slugify.ts deleted file mode 100644 index f325d1f..0000000 --- a/adonis-typings/slugify.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * @adonisjs/lucid-slugify - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare module '@ioc:Adonis/Addons/LucidSlugify' { - import { LucidModel, LucidRow } from '@ioc:Adonis/Lucid/Orm' - import { ApplicationContract } from '@ioc:Adonis/Core/Application' - - /** - * Config accepted by the strategies and the slugify - * decorator - */ - export type SlugifyConfig = { - strategy: keyof StrategiesList | SlugifyStrategyContract - fields: string[] - maxLength?: number - completeWords?: boolean - allowUpdates?: boolean | ((model: LucidRow) => boolean) - separator?: string - transformer?: (value: any) => string - } & Record - - /** - * The interface every strategy must adhere to - */ - export interface SlugifyStrategyContract { - /** - * Make slug for a given field and value - */ - makeSlug(model: LucidModel, field: string, value: string): string - - /** - * Make the slug created by the "makeSlug" method unique. - */ - makeSlugUnique(model: LucidModel, field: string, value: string): Promise | string - } - - /** - * We do not define these in the user land code. Because renaming the - * key inside the following interface doesn't translate that change - * to runtime. - * - * In other words the strategies names are fixed and we use this interface - * to allow other packages to add custom strategies - */ - export interface StrategiesList { - simple: SlugifyStrategyContract - dbIncrement: SlugifyStrategyContract - shortId: SlugifyStrategyContract - } - - /** - * Shape of the extend callback - */ - export type ExtendCallback = ( - manager: SlugifyManagerContract, - config: SlugifyConfig - ) => SlugifyStrategyContract - - /** - * Manager to work - */ - export interface SlugifyManagerContract { - application: ApplicationContract - - /** - * Pull instance of a given strategy - */ - use(strategy: keyof StrategiesList, config: SlugifyConfig): SlugifyStrategyContract - - /** - * Extend by adding a custom strategy - */ - extend(strategy: keyof StrategiesList, callback: ExtendCallback): void - } - - /** - * Slugify decorator - */ - export type SlugifyDecorator = (options?: SlugifyConfig) => (target: any, property: any) => void - - export const slugify: SlugifyDecorator - export const Slugify: SlugifyManagerContract -} diff --git a/bin/test.ts b/bin/test.ts new file mode 100644 index 0000000..5c69cae --- /dev/null +++ b/bin/test.ts @@ -0,0 +1,21 @@ +/* + * @adonisjs/lucid-slugify + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { assert } from '@japa/assert' +import { fileSystem } from '@japa/file-system' +import { configure, processCLIArgs, run } from '@japa/runner' + +processCLIArgs(process.argv.splice(2)) + +configure({ + files: ['tests/**/*.spec.ts'], + plugins: [assert(), fileSystem()], +}) + +run() diff --git a/configure.ts b/configure.ts new file mode 100644 index 0000000..3cddd7d --- /dev/null +++ b/configure.ts @@ -0,0 +1,21 @@ +/* + * @adonisjs/lucid-slugify + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import type Configure from '@adonisjs/core/commands/configure' + +export async function configure(command: Configure) { + const codemods = await command.createCodemods() + + /** + * Publish provider + */ + await codemods.updateRcFile((rcFile) => { + rcFile.addProvider('@adonisjs/lucid-slugify/slugify_provider') + }) +} diff --git a/adonis-typings/index.ts b/index.ts similarity index 58% rename from adonis-typings/index.ts rename to index.ts index 6986832..45cea07 100644 --- a/adonis-typings/index.ts +++ b/index.ts @@ -1,11 +1,10 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/// -/// +export { configure } from './configure.js' diff --git a/package.json b/package.json index 3e97b30..04cce81 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,13 @@ "files": [ "build" ], + "exports": { + ".": "./build/index.js", + "./types": "./build/src/types.js", + "./decorator": "./build/services/slugify.js", + "./services/main": "./build/services/main.js", + "./slugify_provider": "./build/providers/slugify_provider.js" + }, "scripts": { "clean": "del-cli build", "copy:templates": "copyfiles --up 1 \"stubs/**/*.stub\" build", @@ -27,11 +34,11 @@ "test:mssql": "DB=mssql FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", "test:pg": "DB=pg FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", "test:docker": "npm run test:mysql && npm run test:mysql_legacy && npm run test:pg && npm run test:mssql", - "test": "docker-compose -f docker-compose.yml -f docker-compose-test.yml build && docker-compose -f docker-compose.yml -f docker-compose-test.yml run --rm test && npm run test:sqlite", "version": "npm run build", "prepublishOnly": "npm run build" }, "devDependencies": { + "@adonisjs/assembler": "^7.2.3", "@adonisjs/core": "^6.3.1", "@adonisjs/eslint-config": "^1.3.0", "@adonisjs/lucid": "^20.4.0", @@ -48,6 +55,7 @@ "dotenv": "^16.4.5", "eslint": "^8.57.0", "husky": "^9.0.11", + "luxon": "^3.4.4", "mysql": "^2.18.1", "np": "^10.0.1", "pg": "^8.11.3", @@ -111,5 +119,19 @@ "tests/**", "tests_helpers/**" ] + }, + "tsup": { + "entry": [ + "./index.ts", + "./src/types.ts", + "./providers/slugify_provider.ts", + "./services/main.ts", + "./services/slugify.ts" + ], + "outDir": "./build", + "clean": true, + "format": "esm", + "dts": false, + "target": "esnext" } } diff --git a/providers/SlugifyProvider.ts b/providers/SlugifyProvider.ts deleted file mode 100644 index ed2f1f0..0000000 --- a/providers/SlugifyProvider.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * @adonisjs/lucid-slugify - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import { ApplicationContract } from '@ioc:Adonis/Core/Application' - -export default class SlugifyProvider { - constructor(protected application: ApplicationContract) {} - - public register() { - this.application.container.singleton('Adonis/Addons/LucidSlugify', () => { - const { SlugifyManager } = require('../src/SlugifyManager') - const { Slugify } = require('../src/Decorators/Slugify') - - const manager = new SlugifyManager(this.application) - const slugify = new Slugify(manager) - - return { - Slugify: manager, - slugify: slugify.slugifyDecorator.bind(slugify), - } - }) - } -} diff --git a/providers/slugify_provider.ts b/providers/slugify_provider.ts new file mode 100644 index 0000000..7295cbd --- /dev/null +++ b/providers/slugify_provider.ts @@ -0,0 +1,42 @@ +/* + * @adonisjs/lucid-slugify + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/// + +import type { ApplicationService } from '@adonisjs/core/types' + +import { SlugifyDecorator } from '../src/types.js' +import type { SlugifyManager } from '../src/slugify_manager.js' + +declare module '@adonisjs/core/types' { + export interface ContainerBindings { + 'slugify.manager': SlugifyManager + 'slugify.decorator': SlugifyDecorator + } +} + +export default class SlugifyProvider { + constructor(protected app: ApplicationService) {} + + register() { + this.app.container.singleton('slugify.manager', async () => { + const db = await this.app.container.make('lucid.db') + const { SlugifyManager } = await import('../src/slugify_manager.js') + + return new SlugifyManager(db) + }) + + this.app.container.singleton('slugify.decorator', async (resolver) => { + const { Slugify } = await import('../src/decorators/slugify.js') + const slugify = new Slugify(await resolver.make('slugify.manager')) + + return slugify.slugifyDecorator.bind(slugify) + }) + } +} diff --git a/services/main.ts b/services/main.ts new file mode 100644 index 0000000..9ac5454 --- /dev/null +++ b/services/main.ts @@ -0,0 +1,19 @@ +/* + * @adonisjs/lucid-slugify + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import app from '@adonisjs/core/services/app' +import { SlugifyManager } from '../src/slugify_manager.js' + +let slugifyManager: SlugifyManager + +await app.booted(async () => { + slugifyManager = await app.container.make('slugify.manager') +}) + +export { slugifyManager as default } diff --git a/services/slugify.ts b/services/slugify.ts new file mode 100644 index 0000000..267126c --- /dev/null +++ b/services/slugify.ts @@ -0,0 +1,19 @@ +/* + * @adonisjs/lucid-slugify + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import app from '@adonisjs/core/services/app' +import { SlugifyDecorator } from '../src/types.js' + +let slugify: SlugifyDecorator + +await app.booted(async () => { + slugify = await app.container.make('slugify.decorator') +}) + +export { slugify as default } diff --git a/src/SlugifyManager/index.ts b/src/SlugifyManager/index.ts deleted file mode 100644 index d40c359..0000000 --- a/src/SlugifyManager/index.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * @adonisjs/lucid-slugify - * - * (c) Harminder Virk - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/// - -import { Exception } from '@poppinss/utils' -import { ApplicationContract } from '@ioc:Adonis/Core/Application' -import { - SlugifyConfig, - StrategiesList, - ExtendCallback, - SlugifyManagerContract, -} from '@ioc:Adonis/Addons/LucidSlugify' - -/** - * Slugify manager manages the lifecycle of strategies - */ -export class SlugifyManager implements SlugifyManagerContract { - /** - * A private map of strategies added from outside in. - */ - private extendedStrategies: Map = new Map() - - constructor(public application: ApplicationContract) {} - - /** - * Makes an instance of the simple strategy - */ - private makeSimpleStrategy(config: SlugifyConfig) { - const { SimpleStrategy } = require('../Strategies/Simple') - return new SimpleStrategy(config) - } - - /** - * Makes an instance of the dbIncrement strategy - */ - private makeDbIncrementStrategy(config: SlugifyConfig) { - const { DbIncrementStrategy } = require('../Strategies/DbIncrement') - return new DbIncrementStrategy( - this.application.container.resolveBinding('Adonis/Lucid/Database'), - config - ) - } - - /** - * Makes an instance of the shortid strategy - */ - private makeShortIdStrategy(config: SlugifyConfig) { - const { ShortIdStrategy } = require('../Strategies/ShortId') - return new ShortIdStrategy(config) - } - - /** - * Make extended strategy instance - */ - private makeExtendedStrategy(strategy: string, config: SlugifyConfig) { - if (!this.extendedStrategies.has(strategy)) { - throw new Exception( - `"${strategy}" is not a valid slugify strategy`, - 500, - 'E_INVALID_SLUGIFY_STRATEGY' - ) - } - - return this.extendedStrategies.get(strategy)!(this, config) - } - - /** - * Makes an instance of the given strategy - */ - public use(strategy: keyof StrategiesList, config: SlugifyConfig) { - switch (strategy) { - case 'simple': - return this.makeSimpleStrategy(config) - case 'dbIncrement': - return this.makeDbIncrementStrategy(config) - case 'shortId': - return this.makeShortIdStrategy(config) - default: - return this.makeExtendedStrategy(strategy, config) - } - } - - /** - * Extend by adding custom strategies - */ - public extend(strategy: string, callback: ExtendCallback) { - this.extendedStrategies.set(strategy, callback) - } -} diff --git a/src/Decorators/Slugify.ts b/src/decorators/slugify.ts similarity index 79% rename from src/Decorators/Slugify.ts rename to src/decorators/slugify.ts index fe2453a..d871712 100644 --- a/src/Decorators/Slugify.ts +++ b/src/decorators/slugify.ts @@ -1,17 +1,17 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/// +import type { LucidModel, LucidRow } from '@adonisjs/lucid/types/model' -import { LucidModel } from '@ioc:Adonis/Lucid/Orm' -import { SlugifyConfig, SlugifyManagerContract } from '@ioc:Adonis/Addons/LucidSlugify' -import { Slugifier } from '../Slugifier' +import { Slugifier } from '../slugifier.js' +import type { SlugifyConfig } from '../types.js' +import type { SlugifyManager } from '../slugify_manager.js' /** * Slugify classes exposes the "slugifyDecorator" method to be used @@ -20,19 +20,19 @@ import { Slugifier } from '../Slugifier' * to resolve strategies */ export class Slugify { - constructor(private slugifyManager: SlugifyManagerContract) {} + constructor(private slugifyManager: SlugifyManager) {} /** * To be exported from the container as a decorator */ - public slugifyDecorator(config: SlugifyConfig) { + slugifyDecorator(config: SlugifyConfig) { /** * Resolve strategy as soon as someone uses the decorator */ const strategy = - typeof config.strategy === 'string' + typeof config?.strategy === 'string' ? this.slugifyManager.use(config.strategy, config) - : config.strategy + : config?.strategy return function decorateAsSlugify(target: any, property: string) { const Model = target.constructor as LucidModel @@ -45,7 +45,7 @@ export class Slugify { /** * Create slug before the model is persisted */ - Model.before('create', async function (row) { + Model.before('create', async function (row: LucidRow & { [key: string]: any }) { const rowModel = row.constructor as LucidModel /** @@ -65,7 +65,7 @@ export class Slugify { * Create slug before the model is updated, only when allowUpdates * is set to true. */ - Model.before('update', async function (row) { + Model.before('update', async function (row: LucidRow & { [key: string]: any }) { let allowUpdates = config.allowUpdates allowUpdates = typeof allowUpdates === 'function' ? allowUpdates(row) : allowUpdates diff --git a/src/Slugifier/index.ts b/src/slugifier.ts similarity index 78% rename from src/Slugifier/index.ts rename to src/slugifier.ts index 8a0029d..b794535 100644 --- a/src/Slugifier/index.ts +++ b/src/slugifier.ts @@ -1,29 +1,30 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/// +import { LucidModel, LucidRow } from '@adonisjs/lucid/types/model' -import { LucidModel, LucidRow } from '@ioc:Adonis/Lucid/Orm' -import { SlugifyConfig, SlugifyStrategyContract } from '@ioc:Adonis/Addons/LucidSlugify' +import type { SlugifyStrategy, SlugifyConfig } from './types.js' /** * Makes the slug for a given model and field */ export class Slugifier { - private separator = this.config.separator || '-' + private separator: string constructor( - private strategy: SlugifyStrategyContract, + private strategy: SlugifyStrategy, private model: LucidModel, private field: string, private config: SlugifyConfig - ) {} + ) { + this.separator = this.config.separator || '-' + } /** * Transform a given value to a string @@ -55,7 +56,7 @@ export class Slugifier { let hasNullValues: boolean = false for (let field of this.config.fields) { - const value = row[field] + const value = (row as any)[field] if (value === null || value === undefined) { hasNullValues = true break @@ -70,7 +71,7 @@ export class Slugifier { /** * Makes a slug for a given instance of model */ - public async makeSlug(row: LucidRow) { + async makeSlug(row: LucidRow) { const slugValue = this.getSlugValue(row) if (!slugValue) { return null diff --git a/src/slugify_manager.ts b/src/slugify_manager.ts new file mode 100644 index 0000000..3c4f337 --- /dev/null +++ b/src/slugify_manager.ts @@ -0,0 +1,65 @@ +/* + * @adonisjs/lucid-slugify + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Exception } from '@adonisjs/core/exceptions' +import type { Database } from '@adonisjs/lucid/database' + +import { SimpleStrategy } from './strategies/simple.js' +import { ShortIdStrategy } from './strategies/short_id.js' +import { DbIncrementStrategy } from './strategies/db_increment.js' +import type { ExtendCallback, SlugifyConfig, StrategiesList } from './types.js' + +/** + * Slugify manager manages the lifecycle of strategies + */ +export class SlugifyManager { + /** + * A private map of strategies added from outside in. + */ + private extendedStrategies: Map = new Map() + + constructor(protected db: Database) {} + + /** + * Make extended strategy instance + */ + private makeExtendedStrategy(strategy: string, config: SlugifyConfig) { + if (!this.extendedStrategies.has(strategy)) { + throw new Exception(`"${strategy}" is not a valid slugify strategy`, { + code: 'E_INVALID_SLUGIFY_STRATEGY', + status: 500, + }) + } + + return this.extendedStrategies.get(strategy)!(this, config) + } + + /** + * Makes an instance of the given strategy + */ + use(strategy: keyof StrategiesList, config: SlugifyConfig) { + switch (strategy) { + case 'simple': + return new SimpleStrategy(config) + case 'dbIncrement': + return new DbIncrementStrategy(this.db, config) + case 'shortId': + return new ShortIdStrategy(config) + default: + return this.makeExtendedStrategy(strategy, config) + } + } + + /** + * Extend by adding custom strategies + */ + extend(strategy: string, callback: ExtendCallback) { + this.extendedStrategies.set(strategy, callback) + } +} diff --git a/src/Strategies/DbIncrement.ts b/src/strategies/db_increment.ts similarity index 90% rename from src/Strategies/DbIncrement.ts rename to src/strategies/db_increment.ts index cbf95a4..01266b1 100644 --- a/src/Strategies/DbIncrement.ts +++ b/src/strategies/db_increment.ts @@ -1,28 +1,26 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/// - import { Exception } from '@poppinss/utils' -import { LucidModel, LucidRow } from '@ioc:Adonis/Lucid/Orm' -import { DatabaseContract } from '@ioc:Adonis/Lucid/Database' -import { SlugifyConfig, SlugifyStrategyContract } from '@ioc:Adonis/Addons/LucidSlugify' +import type { Database } from '@adonisjs/lucid/database' +import type { LucidModel, LucidRow } from '@adonisjs/lucid/types/model' -import { SimpleStrategy } from './Simple' +import { SimpleStrategy } from './simple.js' +import type { SlugifyConfig, SlugifyStrategy } from '../types.js' /** * Uses a counter variable to make slugs unique */ -export class DbIncrementStrategy extends SimpleStrategy implements SlugifyStrategyContract { +export class DbIncrementStrategy extends SimpleStrategy implements SlugifyStrategy { private counterName = 'lucid_slugify_counter' - constructor(private db: DatabaseContract, config: SlugifyConfig) { + constructor(private db: Database, config: SlugifyConfig) { super(config) } @@ -40,11 +38,11 @@ export class DbIncrementStrategy extends SimpleStrategy implements SlugifyStrate /** * Find the rows that already has a counter */ - const slugs = rows.reduce((result, row) => { + const slugs = rows.reduce((result, row: any) => { const tokens = row[field].toLowerCase().split(`${slug}${this.separator}`) if (tokens.length === 2) { const counter = Number(tokens[1]) - if (!isNaN(counter)) { + if (!Number.isNaN(counter)) { result = result.concat(counter) } } @@ -202,7 +200,7 @@ export class DbIncrementStrategy extends SimpleStrategy implements SlugifyStrate /** * Converts an existing slug to a unique slug by inspecting the database */ - public async makeSlugUnique(model: LucidModel, field: string, slug: string) { + async makeSlugUnique(model: LucidModel, field: string, slug: string) { model.boot() const column = model.$columnsDefinitions.get(field)! @@ -216,6 +214,7 @@ export class DbIncrementStrategy extends SimpleStrategy implements SlugifyStrate case 'redshift': return this.getSlugForPg(model, field, columnName, slug) case 'sqlite3': + case 'better-sqlite3': return this.getSlugForSqlite(model, field, columnName, slug) case 'mysql': return dialectVersion < 8 @@ -228,8 +227,7 @@ export class DbIncrementStrategy extends SimpleStrategy implements SlugifyStrate default: throw new Exception( `"${dialectName}" database is not supported for the dbIncrement strategy`, - 500, - 'E_UNSUPPORTED_DBINCREMENT_DIALECT' + { code: 'E_UNSUPPORTED_DBINCREMENT_DIALECT', status: 500 } ) } } diff --git a/src/Strategies/ShortId.ts b/src/strategies/short_id.ts similarity index 56% rename from src/Strategies/ShortId.ts rename to src/strategies/short_id.ts index 2bbf04f..ab9ef97 100644 --- a/src/Strategies/ShortId.ts +++ b/src/strategies/short_id.ts @@ -1,30 +1,28 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/// - import { nanoid } from 'nanoid' -import { LucidModel } from '@ioc:Adonis/Lucid/Orm' -import { SlugifyStrategyContract } from '@ioc:Adonis/Addons/LucidSlugify' +import type { LucidModel } from '@adonisjs/lucid/types/model' -import { SimpleStrategy } from './Simple' +import { SimpleStrategy } from './simple.js' +import type { SlugifyStrategy } from '../types.js' /** * A Short id strategy that appends a shortid to the base slug */ -export class ShortIdStrategy extends SimpleStrategy implements SlugifyStrategyContract { +export class ShortIdStrategy extends SimpleStrategy implements SlugifyStrategy { protected maxLengthBuffer = 11 /** * Add shortid to the slug */ - public async makeSlugUnique(_: LucidModel, __: string, slug: string) { + async makeSlugUnique(_: LucidModel, __: string, slug: string) { return `${slug}-${nanoid(this.maxLengthBuffer - 1)}` } } diff --git a/src/Strategies/Simple.ts b/src/strategies/simple.ts similarity index 53% rename from src/Strategies/Simple.ts rename to src/strategies/simple.ts index f13d19f..d701378 100644 --- a/src/Strategies/Simple.ts +++ b/src/strategies/simple.ts @@ -1,32 +1,33 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -/// +import string from '@adonisjs/core/helpers/string' +import { LucidModel } from '@adonisjs/lucid/types/model' -import { LucidModel } from '@ioc:Adonis/Lucid/Orm' -import { string } from '@poppinss/utils/build/helpers' -import { SlugifyConfig, SlugifyStrategyContract } from '@ioc:Adonis/Addons/LucidSlugify' +import type { SlugifyConfig, SlugifyStrategy } from '../types.js' /** * A simple strategy to generate slugs */ -export class SimpleStrategy implements SlugifyStrategyContract { - protected separator = this.config.separator || '-' +export class SimpleStrategy implements SlugifyStrategy { + protected separator: string protected maxLengthBuffer = 0 - constructor(private config: SlugifyConfig) {} + constructor(private config: SlugifyConfig) { + this.separator = this.config.separator || '-' + } /** * Makes the slug out the value string */ - public makeSlug(_: LucidModel, __: string, value: string) { - let baseSlug = string.toSlug(value, { + makeSlug(_: LucidModel, __: string, value: string) { + let baseSlug = string.slug(value, { replacement: this.separator, lower: true, strict: true, @@ -48,7 +49,7 @@ export class SimpleStrategy implements SlugifyStrategyContract { /** * Returns the slug as it is */ - public async makeSlugUnique(_: LucidModel, __: string, slug: string) { + async makeSlugUnique(_: LucidModel, __: string, slug: string) { return slug } } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..59e0763 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,55 @@ +import type { LucidModel, LucidRow } from '@adonisjs/lucid/types/model' +import { SlugifyManager } from './slugify_manager.js' + +/** + * The interface every strategy must adhere to + */ +export interface SlugifyStrategy { + /** + * Make slug for a given field and value + */ + makeSlug(model: LucidModel, field: string, value: string): string + + /** + * Make the slug created by the "makeSlug" method unique. + */ + makeSlugUnique(model: LucidModel, field: string, value: string): Promise | string +} + +/** + * Config accepted by the strategies and the slugify + * decorator + */ +export type SlugifyConfig = { + strategy: keyof StrategiesList | SlugifyStrategy + fields: string[] + maxLength?: number + completeWords?: boolean + allowUpdates?: boolean | ((model: LucidRow) => boolean) + separator?: string + transformer?: (value: any) => string +} & Record + +/** + * We do not define these in the user land code. Because renaming the + * key inside the following interface doesn't translate that change + * to runtime. + * + * In other words the strategies names are fixed and we use this interface + * to allow other packages to add custom strategies + */ +export interface StrategiesList { + simple: SlugifyStrategy + dbIncrement: SlugifyStrategy + shortId: SlugifyStrategy +} + +/** + * Shape of the extend callback + */ +export type ExtendCallback = (manager: SlugifyManager, config: SlugifyConfig) => SlugifyStrategy + +/** + * Slugify decorator + */ +export type SlugifyDecorator = (options: SlugifyConfig) => (target: any, property: any) => void diff --git a/tests/configure.spec.ts b/tests/configure.spec.ts new file mode 100644 index 0000000..4df9972 --- /dev/null +++ b/tests/configure.spec.ts @@ -0,0 +1,58 @@ +/* + * @adonisjs/lock + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { test } from '@japa/runner' +import { fileURLToPath } from 'node:url' +import { IgnitorFactory } from '@adonisjs/core/factories' +import Configure from '@adonisjs/core/commands/configure' + +const BASE_URL = new URL('../tmp/', import.meta.url) + +test.group('Configure', (group) => { + group.each.setup(({ context }) => { + context.fs.baseUrl = BASE_URL + context.fs.basePath = fileURLToPath(BASE_URL) + }) + + group.each.timeout(0) + + test('publish provider and env variables', async ({ assert, fs }) => { + const ignitor = new IgnitorFactory() + .withCoreProviders() + .withCoreConfig() + .create(fs.baseUrl, { + importer: (filePath) => { + if (filePath.startsWith('./') || filePath.startsWith('../')) { + return import(new URL(filePath, fs.baseUrl).href) + } + + return import(filePath) + }, + }) + + const app = ignitor.createApp('console') + await app.init() + await app.boot() + + await fs.createJson('tsconfig.json', {}) + await fs.create('adonisrc.ts', `export default defineConfig({})`) + + const ace = await app.container.make('ace') + ace.prompt + .trap('Select the storage layer you want to use') + .assertFails('', 'Please select a store') + .assertPasses('redis') + .chooseOption(1) + + const command = await ace.create(Configure, ['../index.js']) + await command.exec() + + await assert.fileContains('adonisrc.ts', '@adonisjs/lucid-slugify/slugify_provider') + }) +}) diff --git a/test/db-increment-strategy.spec.ts b/tests/db_increment_strategy.spec.ts similarity index 58% rename from test/db-increment-strategy.spec.ts rename to tests/db_increment_strategy.spec.ts index 5a1ff1c..64f271a 100644 --- a/test/db-increment-strategy.spec.ts +++ b/tests/db_increment_strategy.spec.ts @@ -1,47 +1,33 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import test from 'japa' -import { DbIncrementStrategy } from '../src/Strategies/DbIncrement' -import { ApplicationContract } from '@ioc:Adonis/Core/Application' -import { setupApplication, fs, setupDb, cleanDb, clearDb } from '../test-helpers' +import { test } from '@japa/runner' +import { BaseModel } from '@adonisjs/lucid/orm' -let app: ApplicationContract +import { DbIncrementStrategy } from '../src/strategies/db_increment.js' +import { createDatabase, setupDb } from './helpers.js' -test.group('Db Increment Strategy', (group) => { - group.beforeEach(async () => { - app = await setupApplication() - await setupDb(app.container.resolveBinding('Adonis/Lucid/Database')) - }) - - group.afterEach(async () => { - await clearDb(app.container.resolveBinding('Adonis/Lucid/Database')) - }) - - group.after(async () => { - await cleanDb(app.container.resolveBinding('Adonis/Lucid/Database')) - await fs.cleanup() - }) - - test('generate slug', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') +test.group('Db Increment Strategy', () => { + test('generate slug', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } + Post.boot() Post.$addColumn('title', {}) Post.$addColumn('slug', {}) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], }) @@ -49,13 +35,13 @@ test.group('Db Increment Strategy', (group) => { assert.equal(uniqueSlug, 'hello-world') }) - test('add counter to existing duplicate slug', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('add counter to existing duplicate slug', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -76,21 +62,21 @@ test.group('Db Increment Strategy', (group) => { }, ]) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], }) const uniqueSlug = await dbIncrement.makeSlugUnique(Post, 'slug', 'hello-world') - assert.equal(uniqueSlug, 'hello-world-1') + assert.deepEqual(uniqueSlug, 'hello-world-1') }) - test('perform case insensitive search', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('perform case insensitive search', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -111,7 +97,7 @@ test.group('Db Increment Strategy', (group) => { }, ]) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], }) @@ -119,13 +105,13 @@ test.group('Db Increment Strategy', (group) => { assert.equal(uniqueSlug, 'hello-world-1') }) - test('ignore in between numeric values when generating counter', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('ignore in between numeric values when generating counter', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -146,7 +132,7 @@ test.group('Db Increment Strategy', (group) => { }, ]) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], }) @@ -154,13 +140,13 @@ test.group('Db Increment Strategy', (group) => { assert.equal(uniqueSlug, 'post-11am-hello-world-1') }) - test('generate unique slug when counter was manually tweaked', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('generate unique slug when counter was manually tweaked', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -185,7 +171,7 @@ test.group('Db Increment Strategy', (group) => { }, ]) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], }) @@ -194,34 +180,20 @@ test.group('Db Increment Strategy', (group) => { }) }) -test.group('Db Increment Strategy | custom separator', (group) => { - group.beforeEach(async () => { - app = await setupApplication() - await setupDb(app.container.resolveBinding('Adonis/Lucid/Database')) - }) - - group.afterEach(async () => { - await clearDb(app.container.resolveBinding('Adonis/Lucid/Database')) - }) - - group.after(async () => { - await cleanDb(app.container.resolveBinding('Adonis/Lucid/Database')) - await fs.cleanup() - }) - - test('generate slug', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') +test.group('Db Increment Strategy | custom separator', () => { + test('generate slug', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) Post.$addColumn('slug', {}) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], separator: '_', @@ -230,13 +202,13 @@ test.group('Db Increment Strategy | custom separator', (group) => { assert.equal(uniqueSlug, 'hello_world') }) - test('add counter to existing duplicate slug', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('add counter to existing duplicate slug', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -257,7 +229,7 @@ test.group('Db Increment Strategy | custom separator', (group) => { }, ]) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], separator: '_', @@ -266,13 +238,13 @@ test.group('Db Increment Strategy | custom separator', (group) => { assert.equal(uniqueSlug, 'hello_world_1') }) - test('perform case insensitive search', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('perform case insensitive search', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -293,7 +265,7 @@ test.group('Db Increment Strategy | custom separator', (group) => { }, ]) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], separator: '_', @@ -302,13 +274,13 @@ test.group('Db Increment Strategy | custom separator', (group) => { assert.equal(uniqueSlug, 'hello_world_1') }) - test('ignore in between numeric values when generating counter', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('ignore in between numeric values when generating counter', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -329,7 +301,7 @@ test.group('Db Increment Strategy | custom separator', (group) => { }, ]) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], separator: '_', @@ -338,13 +310,13 @@ test.group('Db Increment Strategy | custom separator', (group) => { assert.equal(uniqueSlug, 'post_11am_hello_world_1') }) - test('generate unique slug when counter was manually tweaked', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('generate unique slug when counter was manually tweaked', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -365,7 +337,7 @@ test.group('Db Increment Strategy | custom separator', (group) => { }, ]) - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], separator: '_', diff --git a/test-helpers/index.ts b/tests/helpers.ts similarity index 63% rename from test-helpers/index.ts rename to tests/helpers.ts index 93ab46b..3abfa51 100644 --- a/test-helpers/index.ts +++ b/tests/helpers.ts @@ -1,31 +1,34 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { join } from 'path' import dotenv from 'dotenv' -import { Filesystem } from '@poppinss/dev-utils' -import { Application } from '@adonisjs/core/build/standalone' -import { ConnectionConfig, DatabaseContract } from '@ioc:Adonis/Lucid/Database' +import { join } from 'node:path' +import { getActiveTest } from '@japa/runner' +import { Emitter } from '@adonisjs/core/events' +import { BaseModel } from '@adonisjs/lucid/orm' +import { Database } from '@adonisjs/lucid/database' +import { AppFactory } from '@adonisjs/core/factories/app' +import { LoggerFactory } from '@adonisjs/core/factories/logger' +import { ConnectionConfig } from '@adonisjs/lucid/types/database' dotenv.config() -export const fs = new Filesystem(join(__dirname, '__app')) /** * Returns config based upon DB set in environment variables */ -export function getConfig(): ConnectionConfig { +export function getConfig(basePath: string): ConnectionConfig { switch (process.env.DB) { case 'sqlite': return { client: 'sqlite', connection: { - filename: join(fs.basePath, 'db.sqlite'), + filename: join(basePath, 'db.sqlite'), }, useNullAsDefault: true, debug: false, @@ -91,52 +94,13 @@ export function getConfig(): ConnectionConfig { } /** - * Setup the application + * Setup database initial state for testing */ -export async function setupApplication( - additionalProviders?: string[], - environment: 'web' | 'repl' | 'test' = 'test' -) { - await fs.add('.env', '') - await fs.add( - 'config/app.ts', - ` - export const appKey = 'averylong32charsrandomsecretkey', - export const http = { - cookie: {}, - trustProxy: () => true, - } - ` - ) - - await fs.add( - 'config/database.ts', - `const databaseConfig = { - connection: 'primary', - connections: { - primary: ${JSON.stringify(getConfig())} - } - } - export default databaseConfig` - ) - - const app = new Application(fs.basePath, environment, { - aliases: { - App: './app', - }, - providers: ['@adonisjs/core', '@adonisjs/lucid'].concat(additionalProviders || []), - }) +export async function setupDb(db: Database) { + const test = getActiveTest()! - await app.setup() - await app.registerProviders() - await app.bootProviders() - return app -} + await test.context.fs.mkdir('config') -/** - * Setup database initial state for testing - */ -export async function setupDb(db: DatabaseContract) { const hasPostsTable = await db.connection().schema.hasTable('posts') if (!hasPostsTable) { await db.connection().schema.createTable('posts', (table) => { @@ -148,15 +112,43 @@ export async function setupDb(db: DatabaseContract) { } /** - * Cleandb database post testing + * Clean database post testing */ -export async function cleanDb(_db: DatabaseContract) { - // await db.connection().schema.dropTableIfExists('posts') +export async function cleanDb(db: Database) { + await db.connection().schema.dropTableIfExists('posts') } /** - * Cleardb database post testing + * Clear database post testing */ -export async function clearDb(db: DatabaseContract) { +export async function clearDb(db: Database) { await db.connection().truncate('posts', true) } + +/** + * Create Database instance for testing + */ +export function createDatabase() { + const test = getActiveTest() + if (!test) { + throw new Error('Cannot use "createDatabase" outside of a Japa test') + } + + const app = new AppFactory().create(test.context.fs.baseUrl, () => {}) + const logger = new LoggerFactory().create() + const emitter = new Emitter(app) + const db = new Database( + { + connection: 'primary', + connections: { + primary: getConfig(test.context.fs.basePath), + }, + }, + logger, + emitter + ) + + test.cleanup(() => db.manager.closeAll()) + BaseModel.$adapter = db.modelAdapter() + return db +} diff --git a/test/short-id-strategy.spec.ts b/tests/short_id_strategy.spec.ts similarity index 60% rename from test/short-id-strategy.spec.ts rename to tests/short_id_strategy.spec.ts index 5210dd6..faeefcf 100644 --- a/test/short-id-strategy.spec.ts +++ b/tests/short_id_strategy.spec.ts @@ -1,35 +1,24 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import test from 'japa' -import { ShortIdStrategy } from '../src/Strategies/ShortId' -import { ApplicationContract } from '@ioc:Adonis/Core/Application' -import { setupApplication, fs } from '../test-helpers' +import { test } from '@japa/runner' +import { BaseModel } from '@adonisjs/lucid/orm' -let app: ApplicationContract - -test.group('ShortId Strategy', (group) => { - group.beforeEach(async () => { - app = await setupApplication() - }) - - group.after(async () => { - await fs.cleanup() - }) - - test('generate slug', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') +import { ShortIdStrategy } from '../src/strategies/short_id.js' +test.group('ShortId Strategy', () => { + test('generate slug', async ({ assert }) => { class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } + Post.boot() Post.$addColumn('title', {}) Post.$addColumn('slug', {}) @@ -44,12 +33,10 @@ test.group('ShortId Strategy', (group) => { assert.match(uniqueSlug, /hello-world-.*/) }) - test('slug maxLength must take shortId length into account', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - + test('slug maxLength must take shortId length into account', async ({ assert }) => { class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) diff --git a/test/simple-strategy.spec.ts b/tests/simple_strategy.spec.ts similarity index 60% rename from test/simple-strategy.spec.ts rename to tests/simple_strategy.spec.ts index f6c9814..4130ff4 100644 --- a/test/simple-strategy.spec.ts +++ b/tests/simple_strategy.spec.ts @@ -1,34 +1,22 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import test from 'japa' -import { SimpleStrategy } from '../src/Strategies/Simple' -import { ApplicationContract } from '@ioc:Adonis/Core/Application' -import { setupApplication, fs } from '../test-helpers' +import { test } from '@japa/runner' +import { BaseModel } from '@adonisjs/lucid/orm' -let app: ApplicationContract - -test.group('Simple Strategy', (group) => { - group.beforeEach(async () => { - app = await setupApplication() - }) - - group.after(async () => { - await fs.cleanup() - }) - - test('generate slug', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') +import { SimpleStrategy } from '../src/strategies/simple.js' +test.group('Simple Strategy', () => { + test('generate slug', async ({ assert }) => { class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -39,15 +27,13 @@ test.group('Simple Strategy', (group) => { fields: ['title'], }) - assert.equal(simpleStrategy.makeSlug(Post, 'slug', 'Hello world'), 'hello-world') + assert.deepEqual(simpleStrategy.makeSlug(Post, 'slug', 'Hello world'), 'hello-world') }) - test('trim slug after defined maxLength', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - + test('trim slug after defined maxLength', async ({ assert }) => { class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -68,12 +54,10 @@ test.group('Simple Strategy', (group) => { assert.lengthOf(slug, 40) }) - test('complete words when trimming slug', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - + test('complete words when trimming slug', async ({ assert }) => { class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -95,12 +79,10 @@ test.group('Simple Strategy', (group) => { assert.lengthOf(slug, 45) }) - test('remove single quotes and question marks from the slug', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - + test('remove single quotes and question marks from the slug', async ({ assert }) => { class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) diff --git a/test/slugifier.spec.ts b/tests/slugifier.spec.ts similarity index 57% rename from test/slugifier.spec.ts rename to tests/slugifier.spec.ts index 6596ded..7331672 100644 --- a/test/slugifier.spec.ts +++ b/tests/slugifier.spec.ts @@ -1,43 +1,27 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import test from 'japa' -import { ApplicationContract } from '@ioc:Adonis/Core/Application' +import { test } from '@japa/runner' +import { BaseModel } from '@adonisjs/lucid/orm' -import { Slugifier } from '../src/Slugifier' -import { DbIncrementStrategy } from '../src/Strategies/DbIncrement' -import { setupApplication, fs, setupDb, cleanDb, clearDb } from '../test-helpers' +import { Slugifier } from '../src/slugifier.js' +import { createDatabase, setupDb } from './helpers.js' +import { DbIncrementStrategy } from '../src/strategies/db_increment.js' -let app: ApplicationContract - -test.group('Slugifier', (group) => { - group.beforeEach(async () => { - app = await setupApplication() - await setupDb(app.container.resolveBinding('Adonis/Lucid/Database')) - }) - - group.afterEach(async () => { - await clearDb(app.container.resolveBinding('Adonis/Lucid/Database')) - }) - - group.after(async () => { - await cleanDb(app.container.resolveBinding('Adonis/Lucid/Database')) - await fs.cleanup() - }) - - test('generate slug for a model', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') +test.group('Slugifier', () => { + test('generate slug for a model', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -46,7 +30,7 @@ test.group('Slugifier', (group) => { const post = new Post() post.title = 'hello world' - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], }) @@ -59,13 +43,13 @@ test.group('Slugifier', (group) => { assert.equal(slug, 'hello-world') }) - test('generate unique slug when a similar one already exists', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('generate unique slug when a similar one already exists', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public slug: string + declare title: string + declare slug: string } Post.boot() Post.$addColumn('title', {}) @@ -89,7 +73,7 @@ test.group('Slugifier', (group) => { const post = new Post() post.title = 'hello world' - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title'], }) @@ -102,14 +86,14 @@ test.group('Slugifier', (group) => { assert.equal(slug, 'hello-world-1') }) - test('generate slug from multiple columns', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('generate slug from multiple columns', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public createdAt: string - public slug: string + declare title: string + declare createdAt: string + declare slug: string } Post.boot() @@ -121,7 +105,7 @@ test.group('Slugifier', (group) => { post.title = 'hello world' post.createdAt = '2020-10-20' - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title', 'createdAt'], }) @@ -134,14 +118,14 @@ test.group('Slugifier', (group) => { assert.equal(slug, 'hello-world-2020-10-20') }) - test('do not generate slug when one of the columns are not defined', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('do not generate slug when one of the columns are not defined', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public createdAt: string - public slug: string + declare title: string + declare createdAt: string + declare slug: string } Post.boot() @@ -152,7 +136,7 @@ test.group('Slugifier', (group) => { const post = new Post() post.title = 'hello world' - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title', 'createdAt'], }) @@ -165,14 +149,14 @@ test.group('Slugifier', (group) => { assert.isNull(slug) }) - test('cast booleans to string', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('cast booleans to string', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public isActive: boolean - public slug: string + declare title: string + declare isActive: boolean + declare slug: string } Post.boot() @@ -184,7 +168,7 @@ test.group('Slugifier', (group) => { post.title = 'hello world' post.isActive = true - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title', 'isActive'], }) @@ -197,14 +181,14 @@ test.group('Slugifier', (group) => { assert.equal(slug, 'hello-world-1') }) - test('cast numbers to string', async (assert) => { - const { BaseModel } = app.container.resolveBinding('Adonis/Lucid/Orm') - const Database = app.container.resolveBinding('Adonis/Lucid/Database') + test('cast numbers to string', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) class Post extends BaseModel { - public title: string - public teamId: number - public slug: string + declare title: string + declare teamId: number + declare slug: string } Post.boot() @@ -216,7 +200,7 @@ test.group('Slugifier', (group) => { post.title = 'hello world' post.teamId = 42 - const dbIncrement = new DbIncrementStrategy(Database, { + const dbIncrement = new DbIncrementStrategy(db, { strategy: 'dbIncrement', fields: ['title', 'teamId'], }) diff --git a/test/slugify-decorator.spec.ts b/tests/slugify_decorator.spec.ts similarity index 55% rename from test/slugify-decorator.spec.ts rename to tests/slugify_decorator.spec.ts index 8431b14..0719b6c 100644 --- a/test/slugify-decorator.spec.ts +++ b/tests/slugify_decorator.spec.ts @@ -1,47 +1,38 @@ /* * @adonisjs/lucid-slugify * - * (c) Harminder Virk + * (c) AdonisJS * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import test from 'japa' -import { ApplicationContract } from '@ioc:Adonis/Core/Application' -import { setupApplication, fs, setupDb, cleanDb, clearDb } from '../test-helpers' +import { test } from '@japa/runner' +import { LucidRow } from '@adonisjs/lucid/types/model' +import { BaseModel, column } from '@adonisjs/lucid/orm' -let app: ApplicationContract +import { Slugify } from '../src/decorators/slugify.js' +import { createDatabase, setupDb } from './helpers.js' +import { SlugifyManager } from '../src/slugify_manager.js' -test.group('Slugify Decorator', (group) => { - group.beforeEach(async () => { - app = await setupApplication(['../../providers/SlugifyProvider']) - await setupDb(app.container.resolveBinding('Adonis/Lucid/Database')) - }) - - group.afterEach(async () => { - await clearDb(app.container.resolveBinding('Adonis/Lucid/Database')) - }) - - group.after(async () => { - await cleanDb(app.container.resolveBinding('Adonis/Lucid/Database')) - await fs.cleanup() - }) +test.group('Slugify Decorator', () => { + test('generate slug for a model', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) - test('generate slug for a model', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ strategy: 'dbIncrement', fields: ['title'] }) - public slug: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'] }) + declare slug: string } const post = new Post() @@ -52,20 +43,23 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.slug, 'hello-world') }) - test('do not set slug when defined manually', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('do not set slug when defined manually', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ strategy: 'dbIncrement', fields: ['title'] }) - public slug: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'] }) + declare slug: string } const post = new Post() @@ -77,20 +71,23 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.slug, 'user-defined-slug') }) - test('do not set slug when source is undefined', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('do not set slug when source is undefined', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ strategy: 'dbIncrement', fields: ['title'] }) - public slug: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'] }) + declare slug: string } const post = new Post() @@ -100,20 +97,23 @@ test.group('Slugify Decorator', (group) => { assert.isNull(post.slug) }) - test('generate unique slug when a similar one already exists', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('generate unique slug when a similar one already exists', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column({ columnName: 'slug' }) - @slugify({ strategy: 'dbIncrement', fields: ['title'] }) - public aDifferentPropertyName: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'] }) + declare aDifferentPropertyName: string } await Post.createMany([ @@ -139,20 +139,23 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.aDifferentPropertyName, 'hello-world-1') }) - test('do not update slug when source changes', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('do not update slug when source changes', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ strategy: 'dbIncrement', fields: ['title'] }) - public slug: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'] }) + declare slug: string } await Post.createMany([ @@ -178,20 +181,23 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.slug, 'hello-world') }) - test('update slug when allowUpdates is set to true', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('update slug when allowUpdates is set to true', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ strategy: 'dbIncrement', fields: ['title'], allowUpdates: true }) - public slug: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'], allowUpdates: true }) + declare slug: string } await Post.createMany([ @@ -217,27 +223,31 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.slug, 'a-new-title') }) - test('update slug when allowUpdates function returns true', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('update slug when allowUpdates function returns true', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'], - allowUpdates: (post: Post) => { + allowUpdates: (model: LucidRow) => { + const post = model as Post assert.instanceOf(post, Post) return true }, }) - public slug: string + declare slug: string } await Post.createMany([ @@ -263,27 +273,31 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.slug, 'a-new-title') }) - test('do not update slug when allowUpdates function returns false', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('do not update slug when allowUpdates function returns false', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'], - allowUpdates: (post: Post) => { + allowUpdates: (model: LucidRow) => { + const post = model as Post assert.instanceOf(post, Post) return false }, }) - public slug: string + declare slug: string } await Post.createMany([ @@ -309,20 +323,23 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.slug, 'hello-world') }) - test('do not update slug when defined manually', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('do not update slug when defined manually', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ strategy: 'dbIncrement', fields: ['title'], allowUpdates: true }) - public slug: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'], allowUpdates: true }) + declare slug: string } await Post.createMany([ @@ -349,20 +366,23 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.slug, 'the-old-slug') }) - test('do not update slug when source is untouched', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('do not update slug when source is untouched', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string + declare title: string @column() - @slugify({ strategy: 'dbIncrement', fields: ['title'], allowUpdates: true }) - public slug: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'], allowUpdates: true }) + declare slug: string } await Post.createMany([ @@ -387,20 +407,23 @@ test.group('Slugify Decorator', (group) => { assert.equal(post.slug, 'hello-world') }) - test('do not update slug when source is set to null', async (assert) => { - const { BaseModel, column } = app.container.resolveBinding('Adonis/Lucid/Orm') - const { slugify } = app.container.resolveBinding('Adonis/Addons/LucidSlugify') + test('do not update slug when source is set to null', async ({ assert }) => { + const db = createDatabase() + await setupDb(db) + + const slugifyManager = new SlugifyManager(db) + const slugify = new Slugify(slugifyManager) class Post extends BaseModel { @column({ isPrimary: true }) - public id: number + declare id: number @column() - public title: string | null + declare title: string | null @column() - @slugify({ strategy: 'dbIncrement', fields: ['title'], allowUpdates: true }) - public slug: string + @slugify.slugifyDecorator({ strategy: 'dbIncrement', fields: ['title'], allowUpdates: true }) + declare slug: string } await Post.createMany([ From 83e59856133cc45764a3e52139f06f99198c0974 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Fri, 15 Mar 2024 15:31:30 +0100 Subject: [PATCH 05/12] chore: update readme --- README.md | 54 ++++++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 18c56da..1655872 100644 --- a/README.md +++ b/README.md @@ -23,23 +23,17 @@ Generating slugs is easy, but keeping them unique and within a maximum length ra - Add your custom strategies ## Usage -Install the package from the npm registry as follows: +Install and configure the package as follows: ```sh -npm i @adonisjs/lucid-slugify -``` - -And then configure the package as follows: - -```sh -node ace configure @adonisjs/lucid-slugify +node ace add @adonisjs/lucid-slugify ``` Once done, you need to use the following decorator on the field for which you want to generate the slug. Following is an example with the `Post` model generating slug from the **post title**. ```ts -import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' -import { slugify } from '@ioc:Adonis/Addons/LucidSlugify' +import { BaseModel, column } from '@adonisjs/lucid/orm' +import slugify from '@adonisjs/lucid-slugify/decorator' class Post extends BaseModel { @column({ isPrimary: true }) @@ -205,8 +199,8 @@ If you generate another slug for the **Hello world** title, the `dbIncrement` st #### Model definition ```ts -import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' -import { slugify } from '@ioc:Adonis/Addons/LucidSlugify' +import { BaseModel, column } from '@adonisjs/lucid/orm' +import slugify from '@adonisjs/lucid-slugify/decorator' class Post extends BaseModel { @column({ isPrimary: true }) @@ -255,8 +249,8 @@ The implementation details vary a lot across different database drivers. The `simple` strategy just generates a slug respecting the `maxLength` and `completeWords` config options. No uniqueness is guaranteed when using this strategy. ```ts -import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' -import { slugify } from '@ioc:Adonis/Addons/LucidSlugify' +import { BaseModel, column } from '@adonisjs/lucid/orm' +import slugify from '@adonisjs/lucid-slugify/decorator' class Post extends BaseModel { @column({ isPrimary: true }) @@ -279,8 +273,8 @@ class Post extends BaseModel { The `shortId` strategy **appends a ten-digit long random short id** to the initial slug value for uniqueness. Following is an example of using the `shortId` strategy. ```ts -import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm' -import { slugify } from '@ioc:Adonis/Addons/LucidSlugify' +import { BaseModel, column } from '@adonisjs/lucid/orm' +import slugify from '@adonisjs/lucid-slugify/decorator' class Post extends BaseModel { @column({ isPrimary: true }) @@ -314,9 +308,9 @@ You can add custom strategies using two different ways. The simplest way is to define the strategy inline in the decorator options. A strategy must implement the following two methods. ```ts -import { SlugifyStrategyContract } from '@ioc:Adonis/Addons/LucidSlugify' +import { SlugifyStrategy } from '@adonisjs/lucid-slugify/types -const myCustomStrategy: SlugifyStrategyContract = { +const myCustomStrategy: SlugifyStrategy = { makeSlug (model, field, value) { return // slug for the value }, @@ -332,17 +326,17 @@ const myCustomStrategy: SlugifyStrategyContract = { ``` ### Extending the `slugify` package -This is the recommended approach when you are distributing your strategy as an npm package. Every strategy must implement the `SlugifyStrategyContract` interface. +This is the recommended approach when you are distributing your strategy as an npm package. Every strategy must implement the `SlugifyStrategy` interface. #### Define strategy ```ts import { SlugifyConfig, - SlugifyStrategyContract -} from '@ioc:Adonis/Addons/LucidSlugify' + SlugifyStrategy +} from '@adonisjs/lucid-slugify/types' -class MyStrategy implements SlugifyStrategyContract { +class MyStrategy implements SlugifyStrategy { constructor (private config: SlugifyConfig) {} makeSlug ( @@ -360,18 +354,18 @@ class MyStrategy implements SlugifyStrategyContract { ``` #### Register the strategy -Register the strategy using the `Slugify.extend` method. You must write the following code inside the provider `boot` method. +Register the strategy using the `slugify.extend` method. You must write the following code inside the provider `boot` method. ```ts -import { ApplicationContract } from '@ioc:Adonis/Core/Application' +import type { ApplicationService } from '@adonisjs/core/types' export default class AppProvider { - constructor(protected app: ApplicationContract) {} + constructor(protected app: ApplicationService) {} public async boot() { - const { Slugify } = this.app.container.use('Adonis/Addons/LucidSlugify') + const slugify = await this.app.container.make('slugify.manager') - Slugify.extend('strategyName', (slugify, config) => { + slugify.extend('strategyName', (slugify, config) => { return new MyStrategy(config) }) } @@ -379,12 +373,12 @@ export default class AppProvider { ``` #### Inform typescript about the strategy -Finally, you will also have to inform typescript about the new strategy you added using the `Slugify.extend` method. We will use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces) to add the property to the `StrategiesList` interface. +Finally, you will also have to inform typescript about the new strategy you added using the `slugify.extend` method. We will use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces) to add the property to the `StrategiesList` interface. ```ts -declare module '@ioc:Adonis/Addons/LucidSlugify' { +declare module '@adonisjs/lucid-slugify/types' { interface StrategiesList { - strategyName: SlugifyStrategyContract + strategyName: SlugifyStrategy } } ``` From e0c3b525d5ef6730845f028ed224e6bd5fbd39be Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Fri, 15 Mar 2024 15:45:22 +0100 Subject: [PATCH 06/12] ci: add ci --- .github/COMMIT_CONVENTION.md | 70 ------------- .github/CONTRIBUTING.md | 46 --------- .github/ISSUE_TEMPLATE/bug_report.md | 29 ------ .github/ISSUE_TEMPLATE/feature_request.md | 28 ------ .github/PULL_REQUEST_TEMPLATE.md | 28 ------ .github/workflows/test.yml | 116 +++++++++++++++++++++- package.json | 1 + 7 files changed, 113 insertions(+), 205 deletions(-) delete mode 100644 .github/COMMIT_CONVENTION.md delete mode 100644 .github/CONTRIBUTING.md delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/COMMIT_CONVENTION.md b/.github/COMMIT_CONVENTION.md deleted file mode 100644 index fc852af..0000000 --- a/.github/COMMIT_CONVENTION.md +++ /dev/null @@ -1,70 +0,0 @@ -## Git Commit Message Convention - -> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular). - -Using conventional commit messages, we can automate the process of generating the CHANGELOG file. All commits messages will automatically be validated against the following regex. - -``` js -/^(revert: )?(feat|fix|docs|style|refactor|perf|test|workflow|ci|chore|types|build|improvement)((.+))?: .{1,50}/ -``` - -## Commit Message Format -A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**: - -> The **scope** is optional - -``` -feat(router): add support for prefix - -Prefix makes it easier to append a path to a group of routes -``` - -1. `feat` is type. -2. `router` is scope and is optional -3. `add support for prefix` is the subject -4. The **body** is followed by a blank line. -5. The optional **footer** can be added after the body, followed by a blank line. - -## Types -Only one type can be used at a time and only following types are allowed. - -- feat -- fix -- docs -- style -- refactor -- perf -- test -- workflow -- ci -- chore -- types -- build - -If a type is `feat`, `fix` or `perf`, then the commit will appear in the CHANGELOG.md file. However if there is any BREAKING CHANGE, the commit will always appear in the changelog. - -### Revert -If the commit reverts a previous commit, it should begin with `revert:`, followed by the header of the reverted commit. In the body it should say: `This reverts commit `., where the hash is the SHA of the commit being reverted. - -## Scope -The scope could be anything specifying place of the commit change. For example: `router`, `view`, `querybuilder`, `database`, `model` and so on. - -## Subject -The subject contains succinct description of the change: - -- use the imperative, present tense: "change" not "changed" nor "changes". -- don't capitalize first letter -- no dot (.) at the end - -## Body - -Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". -The body should include the motivation for the change and contrast this with previous behavior. - -## Footer - -The footer should contain any information about **Breaking Changes** and is also the place to -reference GitHub issues that this commit **Closes**. - -**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this. - diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index f0c5446..0000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,46 +0,0 @@ -# Contributing - -AdonisJS is a community driven project. You are free to contribute in any of the following ways. - -- [Coding style](coding-style) -- [Fix bugs by creating PR's](fix-bugs-by-creating-prs) -- [Share an RFC for new features or big changes](share-an-rfc-for-new-features-or-big-changes) -- [Report security issues](report-security-issues) -- [Be a part of the community](be-a-part-of-community) - -## Coding style - -Majority of AdonisJS core packages are written in Typescript. Having a brief knowledge of Typescript is required to contribute to the core. - -## Fix bugs by creating PR's - -We appreciate every time you report a bug in the framework or related libraries. However, taking time to submit a PR can help us in fixing bugs quickly and ensure a healthy and stable eco-system. - -Go through the following points, before creating a new PR. - -1. Create an issue discussing the bug or short-coming in the framework. -2. Once approved, go ahead and fork the REPO. -3. Make sure to start from the `develop`, since this is the upto date branch. -4. Make sure to keep commits small and relevant. -5. We follow [conventional-commits](https://github.com/conventional-changelog/conventional-changelog) to structure our commit messages. Instead of running `git commit`, you must run `npm commit`, which will show you prompts to create a valid commit message. -6. Once done with all the changes, create a PR against the `develop` branch. - -## Share an RFC for new features or big changes - -Sharing PR's for small changes works great. However, when contributing big features to the framework, it is required to go through the RFC process. - -### What is an RFC? - -RFC stands for **Request for Commits**, a standard process followed by many other frameworks including [Ember](https://github.com/emberjs/rfcs), [yarn](https://github.com/yarnpkg/rfcs) and [rust](https://github.com/rust-lang/rfcs). - -In brief, RFC process allows you to talk about the changes with everyone in the community and get a view of the core team before dedicating your time to work on the feature. - -The RFC proposals are created as Pull Request on [adonisjs/rfcs](https://github.com/adonisjs/rfcs) repo. Make sure to read the README to learn about the process in depth. - -## Report security issues - -All of the security issues, must be reported via [email](mailto:virk@adonisjs.com) and not using any of the public channels. - -## Be a part of community - -We welcome you to participate in [GitHub Discussion](https://github.com/adonisjs/core/discussions) and the AdonisJS [Discord Server](https://discord.gg/vDcEjq6). You are free to ask your questions and share your work or contributions made to AdonisJS eco-system. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index e65000c..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: Bug report -about: Report identified bugs ---- - - - -## Prerequisites - -We do our best to reply to all the issues on time. If you will follow the given guidelines, the turn around time will be faster. - -- Lots of raised issues are directly not bugs but instead are design decisions taken by us. -- Make use of our [GH discussions](https://github.com/adonisjs/core/discussions), or [discord server](https://discord.me/adonisjs), if you are not sure that you are reporting a bug. -- Ensure the issue isn't already reported. -- Ensure you are reporting the bug in the correct repo. - -*Delete the above section and the instructions in the sections below before submitting* - -## Package version - - -## Node.js and npm version - - -## Sample Code (to reproduce the issue) - - -## BONUS (a sample repo to reproduce the issue) - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index abd44a5..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Feature request -about: Propose changes for adding a new feature ---- - - - -## Prerequisites - -We do our best to reply to all the issues on time. If you will follow the given guidelines, the turn around time will be faster. - -## Consider an RFC - -Please create an [RFC](https://github.com/adonisjs/rfcs) instead, if - -- Feature introduces a breaking change -- Demands lots of time and changes in the current code base. - -*Delete the above section and the instructions in the sections below before submitting* - -## Why this feature is required (specific use-cases will be appreciated)? - - -## Have you tried any other work arounds? - - -## Are you willing to work on it with little guidance? - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index b6862e9..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,28 +0,0 @@ - - -## Proposed changes - -Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. - -## Types of changes - -What types of changes does your code introduce? - -_Put an `x` in the boxes that apply_ - -- [ ] Bugfix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - -## Checklist - -_Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ - -- [ ] I have read the [CONTRIBUTING](https://github.com/adonisjs/lucid-slugify/blob/master/.github/CONTRIBUTING.md) doc -- [ ] Lint and unit tests pass locally with my changes -- [ ] I have added tests that prove my fix is effective or that my feature works. -- [ ] I have added necessary documentation (if appropriate) - -## Further comments - -If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d2f804..490adab 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,12 +2,120 @@ name: test on: - push - pull_request + jobs: - build: + lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 21 + - run: npm install + - run: npm run lint + + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 21 + - run: npm install + - run: npm run typecheck + + test-postgres: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: [20, 21] + postgres-version: [11, 14] + services: + postgres: + image: postgres:${{ matrix.postgres-version }} + env: + POSTGRES_DB: lucid_slugify + POSTGRES_USER: virk + POSTGRES_PASSWORD: password + ports: + - 5432:5432 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Install + run: npm install + - name: Run Postgres Tests + run: npm run test:pg + + test-mysql: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + mysql: [{ version: '5.7', command: 'mysql_legacy' }, { version: '8.0', command: 'mysql' }] + node-version: [20, 21] + services: + mysql: + image: mysql:${{ matrix.mysql.version }} + env: + MYSQL_DATABASE: lucid + MYSQL_USER: virk + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: password + MYSQL_PORT: 3306 + ports: + - '3306:3306' + - '3308:3306' + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Install + run: npm install + - name: Run Mysql Tests + run: npm run test:${{ matrix.mysql.command }} + + test-sqlite: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + lib: ['sqlite'] + node-version: [20, 21] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Install + run: npm install + - name: Run Sqlite Tests + run: npm run test:${{ matrix.lib }} + + test-mssql: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: [20, 21] + services: + mssql: + image: mcr.microsoft.com/mssql/server:2019-latest + env: + SA_PASSWORD: 'arandom&233password' + ACCEPT_EULA: 'Y' + ports: + - '1433:1433' + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} - name: Install run: npm install - - name: Run tests - run: npm run test + - name: Run Mssql Tests + run: npm run test:mssql diff --git a/package.json b/package.json index 04cce81..7bdb577 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "test:mssql": "DB=mssql FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", "test:pg": "DB=pg FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", "test:docker": "npm run test:mysql && npm run test:mysql_legacy && npm run test:pg && npm run test:mssql", + "test": "c8 npm run test:docker", "version": "npm run build", "prepublishOnly": "npm run build" }, From 592b514bc61c298b75f7eb95f82239ccd95ea5a6 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Fri, 15 Mar 2024 15:49:30 +0100 Subject: [PATCH 07/12] style: lint --- src/strategies/db_increment.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/strategies/db_increment.ts b/src/strategies/db_increment.ts index 01266b1..b6fb9c9 100644 --- a/src/strategies/db_increment.ts +++ b/src/strategies/db_increment.ts @@ -20,7 +20,10 @@ import type { SlugifyConfig, SlugifyStrategy } from '../types.js' export class DbIncrementStrategy extends SimpleStrategy implements SlugifyStrategy { private counterName = 'lucid_slugify_counter' - constructor(private db: Database, config: SlugifyConfig) { + constructor( + private db: Database, + config: SlugifyConfig + ) { super(config) } From a4106978afc6e00e0552714b8a9384597e7d1c9d Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Fri, 15 Mar 2024 15:49:42 +0100 Subject: [PATCH 08/12] chore: fix test scripts --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7bdb577..43afda3 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,11 @@ "compile": "tsup-node && tsc --emitDeclarationOnly --declaration", "build": "tsup-node && tsc --emitDeclarationOnly --declaration && npm run copy:templates", "release": "np", - "test:sqlite": "DB=sqlite FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", - "test:mysql": "DB=mysql FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", - "test:mysql_legacy": "DB=mysql_legacy FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", - "test:mssql": "DB=mssql FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", - "test:pg": "DB=pg FORCE_COLOR=true node --enable-source-maps --loader--loader=ts-node/esm ./bin/test.js", + "test:sqlite": "DB=sqlite FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", + "test:mysql": "DB=mysql FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", + "test:mysql_legacy": "DB=mysql_legacy FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", + "test:mssql": "DB=mssql FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", + "test:pg": "DB=pg FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", "test:docker": "npm run test:mysql && npm run test:mysql_legacy && npm run test:pg && npm run test:mssql", "test": "c8 npm run test:docker", "version": "npm run build", From 2a74414c8593fe1a1486baeb56feb9256b65288d Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Fri, 15 Mar 2024 16:37:53 +0100 Subject: [PATCH 09/12] ci: fix --- .env | 8 ++++---- .github/workflows/test.yml | 12 +++++------- package.json | 5 +++-- tests/helpers.ts | 19 +++++-------------- 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/.env b/.env index 16415ac..0e97e63 100644 --- a/.env +++ b/.env @@ -1,21 +1,21 @@ DB=pg DB_NAME=lucid_slugify -MYSQL_HOST=mysql +MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USER=virk MYSQL_PASSWORD=password -MYSQL_LEGACY_HOST=mysql_legacy +MYSQL_LEGACY_HOST=localhost MYSQL_LEGACY_PORT=3306 MYSQL_LEGACY_USER=virk MYSQL_LEGACY_PASSWORD=password -PG_HOST=pg +PG_HOST=localhost PG_PORT=5432 PG_USER=virk PG_PASSWORD=password -MSSQL_SERVER=mssql +MSSQL_SERVER=localhost MSSQL_USER=sa MSSQL_PASSWORD=arandom&233password diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 490adab..9f01460 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,10 +29,9 @@ jobs: strategy: fail-fast: false matrix: - node-version: [20, 21] - postgres-version: [11, 14] + postgres-version: [15] services: - postgres: + pg: image: postgres:${{ matrix.postgres-version }} env: POSTGRES_DB: lucid_slugify @@ -55,13 +54,12 @@ jobs: strategy: fail-fast: false matrix: - mysql: [{ version: '5.7', command: 'mysql_legacy' }, { version: '8.0', command: 'mysql' }] node-version: [20, 21] services: mysql: - image: mysql:${{ matrix.mysql.version }} + image: mysql:8.0 env: - MYSQL_DATABASE: lucid + MYSQL_DATABASE: lucid_slugify MYSQL_USER: virk MYSQL_PASSWORD: password MYSQL_ROOT_PASSWORD: password @@ -77,7 +75,7 @@ jobs: - name: Install run: npm install - name: Run Mysql Tests - run: npm run test:${{ matrix.mysql.command }} + run: npm run test:mysql test-sqlite: runs-on: ubuntu-latest diff --git a/package.json b/package.json index 43afda3..c5ddc19 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,9 @@ "release": "np", "test:sqlite": "DB=sqlite FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", "test:mysql": "DB=mysql FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", - "test:mysql_legacy": "DB=mysql_legacy FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", "test:mssql": "DB=mssql FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", "test:pg": "DB=pg FORCE_COLOR=true node --enable-source-maps --loader=ts-node/esm ./bin/test.js", - "test:docker": "npm run test:mysql && npm run test:mysql_legacy && npm run test:pg && npm run test:mssql", + "test:docker": "npm run test:mysql && npm run test:pg && npm run test:mssql", "test": "c8 npm run test:docker", "version": "npm run build", "prepublishOnly": "npm run build" @@ -51,6 +50,7 @@ "@japa/runner": "^3.1.1", "@swc/core": "^1.4.8", "@types/node": "^20.11.27", + "c8": "^9.1.0", "copyfiles": "^2.4.1", "del-cli": "^5.1.0", "dotenv": "^16.4.5", @@ -58,6 +58,7 @@ "husky": "^9.0.11", "luxon": "^3.4.4", "mysql": "^2.18.1", + "mysql2": "^3.9.2", "np": "^10.0.1", "pg": "^8.11.3", "prettier": "^3.2.5", diff --git a/tests/helpers.ts b/tests/helpers.ts index 3abfa51..3e94729 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -35,7 +35,7 @@ export function getConfig(basePath: string): ConnectionConfig { } case 'mysql': return { - client: 'mysql', + client: 'mysql2', connection: { host: process.env.MYSQL_HOST as string, port: Number(process.env.MYSQL_PORT), @@ -45,19 +45,6 @@ export function getConfig(basePath: string): ConnectionConfig { }, useNullAsDefault: true, } - case 'mysql_legacy': - return { - client: 'mysql', - version: '5.7', - connection: { - host: process.env.MYSQL_LEGACY_HOST as string, - port: Number(process.env.MYSQL_LEGACY_PORT), - database: process.env.DB_NAME as string, - user: process.env.MYSQL_LEGACY_USER as string, - password: process.env.MYSQL_LEGACY_PASSWORD as string, - }, - useNullAsDefault: true, - } case 'pg': return { client: 'pg', @@ -109,6 +96,10 @@ export async function setupDb(db: Database) { table.string('slug').nullable() }) } + + test.cleanup(async () => { + await db.connection().schema.dropTableIfExists('posts') + }) } /** From 9c74926a152f844fda7947ce43b49abdd8f04d95 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Fri, 15 Mar 2024 16:43:29 +0100 Subject: [PATCH 10/12] refactor: remove useless optional chaining --- src/decorators/slugify.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/decorators/slugify.ts b/src/decorators/slugify.ts index d871712..bb50cd8 100644 --- a/src/decorators/slugify.ts +++ b/src/decorators/slugify.ts @@ -30,9 +30,9 @@ export class Slugify { * Resolve strategy as soon as someone uses the decorator */ const strategy = - typeof config?.strategy === 'string' + typeof config.strategy === 'string' ? this.slugifyManager.use(config.strategy, config) - : config?.strategy + : config.strategy return function decorateAsSlugify(target: any, property: string) { const Model = target.constructor as LucidModel From c50c7a3e4a2cf925195dfa892fae97d134d0a04c Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Wed, 20 Mar 2024 20:31:38 +0100 Subject: [PATCH 11/12] chore(release): 3.0.0-0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c5ddc19..bd67fc8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@adonisjs/lucid-slugify", "description": "Generate unique slugs for your Lucid models", - "version": "2.2.1", + "version": "3.0.0-0", "engines": { "node": ">=20.6.0" }, From 8b6ff24a9881b2478a3a1090b1032bd68037fdd3 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Mon, 12 Aug 2024 10:47:32 +0200 Subject: [PATCH 12/12] docs: correct missing quote in README code snippet (#26) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1655872..2e1ec9c 100644 --- a/README.md +++ b/README.md @@ -308,7 +308,7 @@ You can add custom strategies using two different ways. The simplest way is to define the strategy inline in the decorator options. A strategy must implement the following two methods. ```ts -import { SlugifyStrategy } from '@adonisjs/lucid-slugify/types +import { SlugifyStrategy } from '@adonisjs/lucid-slugify/types' const myCustomStrategy: SlugifyStrategy = { makeSlug (model, field, value) {