diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 3cd5493..bc66554 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,7 @@ # These are supported funding model platforms github: [Next2D] -patreon: next2d +patreon: # next2d open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index fb4d2a5..6285f6b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,12 +13,12 @@ name: "CodeQL" on: push: - branches: [ main, develop ] + branches: [ "main", "develop" ] pull_request: # The branches below must be a subset of the branches above - branches: [ main, develop ] + branches: [ "main", "develop" ] schedule: - - cron: '38 23 * * 1' + - cron: '44 22 * * 5' jobs: analyze: @@ -32,10 +32,11 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + language: [ 'javascript-typescript' ] + # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] + # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository diff --git a/.gitignore b/.gitignore index 8fea6bf..54f07af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,24 @@ -.DS_Store -.idea -.nvmrc -Thumbs.db -coverage +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + node_modules -Footer.build.file -Header.build.file -next2d-framework.js -package-lock.json -dist \ No newline at end of file +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? \ No newline at end of file diff --git a/.npmignore b/.npmignore index 80b7c10..8ece253 100644 --- a/.npmignore +++ b/.npmignore @@ -5,10 +5,8 @@ .github node_modules __tests__ -coverage src Framework_Flowchart.svg -babel.config.js -jest.config.js -jest.setup.js -webpack.config.js \ No newline at end of file +tsconfig.eslint.json +tsconfig.json +vite.config.ts \ No newline at end of file diff --git a/DOCS.md b/DOCS.md deleted file mode 100644 index ea6f74c..0000000 --- a/DOCS.md +++ /dev/null @@ -1,144 +0,0 @@ -#### Quick Start - -${{ framework-api.detail }}$ - -```sh -npx create-next2d-app app-name -cd app-name -npm start -``` - -#### ${{ framework-api.dir-detail }}$ - -```sh -project -├── src -│ ├── index.js -│ ├── App.js -│ ├── Packages.js // It will be generated automatically. -│ │ -│ ├── component -│ │ └── default empty -│ │ -│ ├── config -│ │ ├── config.json // Configuration files for each environment. -│ │ ├── routing.json // Request settings before loading the view. -│ │ ├── stage.json // Display(Stage) area setting. -│ │ └── Config.js // It will be generated automatically. -│ │ -│ ├── content // Symbolic access to JSON created with NoCode Tool -│ │ ├── TopContent.js -│ │ └── HomeContent.js -│ │ -│ ├── image -│ │ └── default empty -│ │ -│ ├── model // business logic -│ │ ├── api -│ │ │ └── HomeText.js -│ │ └── callback -│ │ └── Background.js -│ │ -│ └── view // Per-page View, ViewModel files. -│ ├── top -│ │ ├── TopView.js -│ │ └── TopViewModel.js -│ └── home -│ ├── HomeView.js -│ └── HomeViewModel.js -│ -└── __tests__ // Unit Test directory - └── model - └── default empty -``` - -#### ${{ framework-api.chart-detail }}$ - - -#### ${{ framework-api.command.title }}$ - -* ${{ framework-api.command.text1 }}$ -```sh -npm start -``` - -* ${{ framework-api.command.text2 }}$ -```sh -npm run generate -``` - -* ${{ framework-api.command.text3 }}$ -```sh -npm run build -- --env="prd" -``` - -* ${{ framework-api.command.text4 }}$ -```sh -npm test -``` - -### ${{ framework-api.conf-detail }}$ - -#### stage.json - -| ${{ player-api.options.th1 }}$ | ${{ player-api.options.th2 }}$ | ${{ player-api.options.th3 }}$ | ${{ player-api.options.th4 }}$ | -| --- | --- | --- | --- | -| `width` | number | 240 | ${{ framework-api.stage.text1 }}$ | -| `height` | number | 240 | ${{ framework-api.stage.text2 }}$ | -| `fps` | number | 12 |${{ framework-api.stage.text3 }}$ | - -##### Option settings - -| ${{ player-api.options.th1 }}$ | ${{ player-api.options.th2 }}$ | ${{ player-api.options.th3 }}$ | ${{ player-api.options.th4 }}$ | -| --- | --- | --- | --- | -| `base` | string | empty | ${{ player-api.options.text1 }}$ | -| `fullScreen` | boolean | false | ${{ player-api.options.text2 }}$ | -| `tagId` | string | null | ${{ player-api.options.text3 }}$ | -| `bgColor` | array | null | ${{ player-api.options.text4 }}$ | - -#### config.json - -| ${{ framework-api.config.detail }}$ - -| ${{ player-api.options.th1 }}$ | ${{ player-api.options.th2 }}$ | ${{ player-api.options.th3 }}$ | ${{ player-api.options.th4 }}$ | -| --- | --- | --- | --- | -| `spa` | boolean | true | ${{ framework-api.config.text1 }}$ | -| `loading`.`callback` | string | Loading | ${{ framework-api.config.text2 }}$ | -| `gotoView`.`callback` | string or array | ["callback.Background"] | ${{ framework-api.config.text3 }}$ | - -#### routing.json - -${{ framework-api.routing.detail }}$ - -##### Example - -${{ framework-api.routing.example }}$ - -```json -{ - "quest/list": { - "requests": [] - } -} -```` - -#### ${{ framework-api.routing.detail2 }}$ - -| ${{ player-api.options.th1 }}$ | ${{ player-api.options.th2 }}$ | ${{ player-api.options.th3 }}$ | ${{ player-api.options.th4 }}$ | -| --- | --- | --- | --- | -| `private` | boolean | false | ${{ framework-api.routing.text1 }}$ | -| `redirect` | string | empty | ${{ framework-api.routing.text2 }}$ | -| `requests` | array | null | ${{ framework-api.routing.text3 }}$ | - -#### ${{ framework-api.routing.detail3 }}$ - -| ${{ player-api.options.th1 }}$ | ${{ player-api.options.th2 }}$ | ${{ player-api.options.th3 }}$ | ${{ player-api.options.th4 }}$ | -| --- | --- | --- | --- | -| `type` | string | `content` | ${{ framework-api.routing.text4 }}$ | -| `path` | string | empty | ${{ framework-api.routing.text5 }}$ | -| `name` | string | empty | ${{ framework-api.routing.text6 }}$ | -| `cache` | boolean | false | ${{ framework-api.routing.text7 }}$ | -| `callback` | string or array | null | ${{ framework-api.routing.text8 }}$ | -| `class` | string | empty | ${{ framework-api.routing.text9 }}$ | -| `access` | string | `public` | ${{ framework-api.routing.text10 }}$ | -| `method` | string | empty | ${{ framework-api.routing.text11 }}$ | diff --git a/LICENSE b/LICENSE index a536abe..d520559 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Next2D +Copyright (c) 2021 - 2023 Next2D Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 72bf3f7..0000000 --- a/babel.config.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "current" - } - } - ] - ], - "plugins": [ - "@babel/plugin-transform-modules-commonjs" - ] -}; \ No newline at end of file diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 9f4a611..0000000 --- a/jest.config.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - "preset": "ts-jest", - "setupFilesAfterEnv": ["./jest.setup.js"], - "moduleNameMapper": { - "^\\@/(.+)": "/src/$1" - }, - "transformIgnorePatterns": [ - "/node_modules/(?!(next2d|@next2d))" - ], - "transform": { - "\\.jsx?$": "babel-jest", - "^.+\\.(ts|tsx)$": "ts-jest" - }, - "testEnvironment": "node" -}; diff --git a/jest.setup.js b/jest.setup.js deleted file mode 100644 index 34a5181..0000000 --- a/jest.setup.js +++ /dev/null @@ -1,487 +0,0 @@ -globalThis.$windowEventMap = new Map(); -globalThis.OffscreenCanvas = class OffscreenCanvas -{ - getContext () - { - return {}; - } -}; - -globalThis.cancelAnimationFrame = () => -{ - return undefined; -}; - -const xhrMockClass = () => ({ - "open" : jest.fn(), - "send" : jest.fn(), - "setRequestHeader": jest.fn(), - "addEventListener": jest.fn() -}); - -globalThis.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass); - -globalThis.window = { - "document": { - "createElement": () => - { - const attribute = new Map(); - return { - "getAttribute": (name) => - { - return attribute.has(name) - ? attribute.get(name) - : null; - }, - "setAttribute": (name, value) => - { - attribute.set(name, value); - }, - "insertBefore": (element) => - { - if (element.id) { - globalThis.$elements.set(element.id, element); - } - }, - "getContext": () => { - return { - "measureText": () => { - return { - "width": 0 - }; - } - }; - }, - "style": {}, - "children": [] - }; - } - }, - "devicePixelRatio": 2, - "OffscreenCanvas": () => - { - return undefined; - }, - "addEventListener": (type, callback) => - { - globalThis.$windowEventMap.set(type, callback); - }, - "next2d": { - "createRootMovieClip": () => { - return Promise.resolve(); - }, - "display": { - "MovieClip": class MovieClip - { - constructor() - { - this.name = ""; - this.namespace = "next2d.display.MovieClip"; - } - - _$sync () - { - return undefined; - } - - _$getChildren () - { - return undefined; - } - }, - "Loader": class Loader - { - constructor () - { - this.event = new Map(); - } - - get contentLoaderInfo () - { - return { - "addEventListener": (name, callback) => - { - this.event.set(name, callback); - } - }; - } - - loadImage () - { - this.event.get("complete")({ - "currentTarget": { - "content": { - "_$loaderInfo": { - "_$data": { - "symbols": new Map([["app", "app"]]) - } - }, - "text": "NoCode Tool image content" - } - } - }); - } - - load () - { - this.event.get("complete")({ - "currentTarget": { - "content": { - "_$loaderInfo": { - "_$data": { - "symbols": new Map([["app", "app"]]) - } - }, - "text": "NoCode Tool content" - } - } - }); - } - }, - "Shape": class Shape - { - get graphics () - { - return this; - } - - beginBitmapFill () - { - return this; - } - - beginFill () - { - return this; - } - - drawRect () - { - return this; - } - - endFill () - { - return this; - } - }, - "BitmapData": class BitmapData - { - draw (source, matrix, color_transform, canvas = {}, callback) - { - callback(canvas); - } - }, - "Sprite": class Sprite - { - addChild (display_object) - { - return display_object; - } - - get graphics () - { - return this; - } - - beginFill () - { - return this; - } - - drawRect () - { - return this; - } - - endFill () - { - return this; - } - } - }, - "player": { - "x": 0, - "y": 0, - "scaleX": 1, - "scaleY": 1 - } - } -}; - -globalThis.next2d = { - "createRootMovieClip": function () - { - const object = { - "_$created": true, - "_$createWorkerInstance": () => - { - return undefined; - }, - "stage": { - "canvasWidth": 100, - "canvasHeight": 100, - "_$player": { - "_$matrix": [1,0,0,1,10,10] - } - }, - "addChild": (child) => - { - return child; - }, - "numChildren": 1, - "removeChild": (number) => - { - object.numChildren = number; - }, - "getChildAt": () => - { - return 0; - } - }; - - return object; - }, - "display": { - "MovieClip": class MovieClip - { - constructor() - { - this.name = ""; - this.namespace = "next2d.display.MovieClip"; - } - - _$sync () - { - return undefined; - } - - _$getChildren () - { - return undefined; - } - }, - "Loader": class Loader - { - constructor () - { - this.event = new Map(); - } - - get contentLoaderInfo () - { - return { - "addEventListener": (name, callback) => - { - this.event.set(name, callback); - } - }; - } - - loadImage () - { - this.event.get("complete")({ - "currentTarget": { - "content": { - "_$loaderInfo": { - "_$data": { - "symbols": new Map([["app", "app"]]) - } - }, - "text": "NoCode Tool image content" - } - } - }); - } - - load () - { - this.event.get("complete")({ - "currentTarget": { - "content": { - "_$loaderInfo": { - "_$data": { - "symbols": new Map([["app", "app"]]) - } - }, - "text": "NoCode Tool content" - } - } - }); - } - }, - "Shape": class Shape - { - get graphics () - { - return this; - } - - beginBitmapFill () - { - return this; - } - - beginFill () - { - return this; - } - - drawRect () - { - return this; - } - - endFill () - { - return this; - } - }, - "BitmapData": class BitmapData - { - draw (source, matrix, color_transform, canvas = {}, callback) - { - callback(canvas); - } - }, - "Sprite": class Sprite - { - addChild (display_object) - { - return display_object; - } - - get graphics () - { - return this; - } - - beginFill () - { - return this; - } - - drawRect () - { - return this; - } - - endFill () - { - return this; - } - } - }, - "geom": { - "Matrix": class Matrix - { - - } - }, - "events": { - "Event": class Event { - static get COMPLETE () - { - return "complete"; - } - static get REMOVED () - { - return "removed"; - } - }, - "IOErrorEvent": class IOErrorEvent - { - static get IO_ERROR () { - return "io_error"; - } - } - }, - "net": { - "URLRequest": class URLRequest - { - constructor() - { - this.method = "GET"; - this.requestHeaders = []; - this.data = null; - } - }, - "URLRequestHeader": class URLRequestHeader - { - constructor (name, value) - { - this.name = name; - this.value = value; - } - }, - "URLRequestMethod": { - "GET": "GET", - "PUT": "PUT", - "POST": "POST" - } - }, - "fw": { - "loaderInfo": new Map(), - "application": "app", - "cache": new Map([["cache", "cache"]]), - "config": { - "stage": { - "width": 240, - "height": 240, - "fps": 12, - "options": {} - } - }, - "context": "context", - "packages": new Map([["class", "class"]]), - "response": new Map([["response", "response"]]), - "variable": new Map([["variable", "variable"]]), - "query": new Map([["query", "query"]]) - } -}; - -globalThis.location = { - "pathname": "/" -}; - -globalThis.history = { - "pushState": () => {} -}; - -globalThis.$elements = new Map(); -globalThis.document = { - "getElementById": (id) => - { - return globalThis.$elements.has(id) - ? globalThis.$elements.get(id) - : null; - }, - "createElement": () => - { - const attribute = new Map(); - return { - "getAttribute": (name) => - { - return attribute.has(name) - ? attribute.get(name) - : null; - }, - "setAttribute": (name, value) => - { - attribute.set(name, value); - }, - "insertBefore": (element) => - { - if (element.id) { - globalThis.$elements.set(element.id, element); - } - }, - "children": [] - }; - } -}; - -globalThis.requestAnimationFrame = (callback) => -{ - return callback(); -}; \ No newline at end of file diff --git a/jsdoc.conf.js b/jsdoc.conf.js deleted file mode 100644 index 086d3b3..0000000 --- a/jsdoc.conf.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; - -module.exports = { - "plugins": [ - "plugins/markdown" - ], - "markdown": { - "hardwrap": true - }, - "templates": { - "cleverLinks" : false, - "monospaceLinks": false, - "applicationName": "Next2D Framework", - "path": "../../../", - "openGraph": { - "title": "Framework API Documentation", - "description": "Framework API Documentation.", - "type": "website", - "image": "https://next2d.app/assets/img/ogp.png", - "url": "https://next2d.app/" - }, - "meta": { - "title": "Framework API Documentation", - "description": "Next2D Framework API Documentation.", - "keyword": "Next2D, WebGL, JavaScript, HTML5, MVVM, DDD, CleanArchitecture" - }, - "linenums": true, - "default" : { - "outputSourceFiles" : true - } - }, - "opts": { - "encoding": "utf8", - "recurse": true, - "private": false, - "lenient": true, - "destination": "../next2d/dist/docs/framework/", - "template": "node_modules/@next2d/jsdoc-template" - } -}; diff --git a/package.json b/package.json index 24db33a..c9cce14 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,14 @@ { "name": "@next2d/framework", - "description": "It is a framework dedicated to Next2D that enables scene management by URL (SPA), which has been difficult with conventional Canvas/WebGL applications, and simplifies readability and shareability by fixing the development pattern (MVVM).", - "version": "1.6.2", + "description": "Next2D Framework is designed according to the principles of clean architecture, domain-driven development, test-driven development, and MVVM, with an emphasis on flexibility, scalability, and maintainability, and a design methodology that keeps each layer loosely coupled.", + "version": "2.0.0", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Framework/issues/new", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "main": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", "keywords": [ "Next2D", "Next2D Framework" @@ -14,10 +16,8 @@ "scripts": { "lint": "eslint src/**/*.ts", "publish": "node ./scripts/version.js && tsc", - "test": "jest", - "jsdoc": "tsc && jsdoc -c jsdoc.conf.js -r dist DOCS.md" + "test": "npx vitest" }, - "types": "dist", "files": [ "dist" ], @@ -25,26 +25,14 @@ "type": "git", "url": "git+https://github.com/Next2D/Framework.git" }, - "dependencies": { - "@next2d/player": "*" - }, "devDependencies": { - "@babel/core": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/preset-env": "^7.22.5", - "@next2d/jsdoc-template": "^1.0.7", - "@types/jest": "^29.5.2", - "@typescript-eslint/eslint-plugin": "^5.60.0", - "@typescript-eslint/parser": "^5.60.0", - "eslint": "^8.43.0", - "eslint-webpack-plugin": "^4.0.1", - "jest": "^29.5.0", - "jsdoc": "^4.0.2", - "ts-jest": "^29.1.0", - "ts-loader": "^9.4.3", - "ts-node": "^10.9.1", - "typescript": "^5.1.3", - "webpack": "^5.88.0", - "webpack-cli": "^5.1.4" + "@next2d/player": "*", + "@typescript-eslint/eslint-plugin": "^6.9.1", + "@typescript-eslint/parser": "^6.9.1", + "eslint": "^8.53.0", + "jsdom": "^22.1.0", + "typescript": "^5.2.2", + "vite": "^4.5.0", + "vitest": "^0.34.6" } } diff --git a/scripts/version.js b/scripts/version.js index 287a9dd..828391e 100644 --- a/scripts/version.js +++ b/scripts/version.js @@ -2,7 +2,7 @@ "use strict"; -const fs = require("fs"); +import * as fs from "fs"; /** * @param {string} dir @@ -16,7 +16,9 @@ const execute = () => if (fs.existsSync(indexPath)) { const src = fs.readFileSync(indexPath, "utf8"); - const packageJson = require(`${process.cwd()}/package.json`); + const packageJson = JSON.parse( + fs.readFileSync(`${process.cwd()}/package.json`, { "encoding": "utf8" }) + ); const texts = src.split("\n"); for (let idx = 0; idx < texts.length; ++idx) { diff --git a/src/application/Application.ts b/src/application/Application.ts index 71f7b5b..b9f3d66 100644 --- a/src/application/Application.ts +++ b/src/application/Application.ts @@ -1,21 +1,29 @@ -import { QueryParser } from "../domain/parser/QueryParser"; -import { RequestUseCase } from "../infrastructure/usecase/RequestUseCase"; -import { Callback } from "../domain/callback/Callback"; -import { Loading } from "../domain/loading/Loading"; -import { Capture } from "../domain/screen/Capture"; -import { RemoveResponse } from "./service/RemoveResponse"; -import { $setPackages } from "./variable/Packages"; -import { config, $setConfig } from "./variable/Config"; -import { context, $createContext } from "./variable/Context"; -import { response } from "./variable/Response"; import type { ResponseDTO } from "../infrastructure/dto/ResponseDTO"; import type { View } from "../view/View"; import type { ConfigImpl } from "../interface/ConfigImpl"; - -interface QueryObject { - name: string; - queryString: string; -} +import type { QueryObjectImpl } from "../interface/QueryObjectImpl"; +import { execute as queryParser } from "../domain/parser/QueryParser"; +import { execute as requestUseCase } from "../infrastructure/usecase/RequestUseCase"; +import { execute as callback } from "../domain/callback/Callback"; +import { + execute as captureExecute, + dispose as captureDispose +} from "../domain/screen/Capture"; +import { execute as removeResponse } from "./service/RemoveResponse"; +import { $setPackages } from "./variable/Packages"; +import { response } from "./variable/Response"; +import { + config, + $setConfig +} from "./variable/Config"; +import { + context, + $createContext +} from "./variable/Context"; +import { + start as loadingStart, + end as loadingEnd +} from "../domain/loading/Loading"; /** * シーン遷移のコントロールを行うクラス。 @@ -26,12 +34,6 @@ interface QueryObject { */ export class Application { - private readonly _$queryParser: QueryParser; - private readonly _$requestUseCase: RequestUseCase; - private readonly _$callback: Callback; - private readonly _$loading: Loading; - private readonly _$capture: Capture; - private readonly _$removeResponse: RemoveResponse; private _$popstate: boolean; private _$currentName: string; @@ -39,72 +41,49 @@ export class Application * @constructor * @public */ - constructor (config: ConfigImpl, packages: any[]) + constructor () { - $setConfig(config); - $setPackages(packages); - /** - * @type {QueryParser} - * @private - */ - this._$queryParser = new QueryParser(); - - /** - * @type {RequestUseCase} - * @private - */ - this._$requestUseCase = new RequestUseCase(); - - /** - * @type {Callback} - * @private - */ - this._$callback = new Callback(); - - /** - * @type {Loading} - * @private - */ - this._$loading = new Loading(); - - /** - * @type {Capture} + * @type {boolean} + * @default false * @private */ - this._$capture = new Capture(); + this._$popstate = false; /** - * @type {RemoveResponse} + * @type {string} + * @default "top" * @private */ - this._$removeResponse = new RemoveResponse(); + this._$currentName = "top"; + } - /** - * @type {boolean} - * @default false - * @private - */ - this._$popstate = false; + /** + * @description 初期起動関数 + * initial invoking function + * + * @return {Application} + * @method + * @public + */ + initialize (config: ConfigImpl, packages: any[]): Application + { + $setConfig(config); + $setPackages(packages); /** * SPAが有効の場合は、遷移の履歴を残す * Keep history of transitions if SPA setting is enabled */ if (config.spa) { - window.addEventListener("popstate", () => + window.addEventListener("popstate", (): void => { this._$popstate = true; this.gotoView(); }); } - /** - * @type {string} - * @default "top" - * @private - */ - this._$currentName = "top"; + return this; } /** @@ -130,121 +109,107 @@ export class Application * @method * @public */ - gotoView (name: string = ""): Promise + async gotoView (name: string = ""): Promise { - const promises: Promise[] = []; if (config.loading) { - /** - * ローディング表示を起動 - * Launch loading display - */ - this._$loading.start(); + + const promises: Promise[] = []; /** * 現時点の描画をBitmapにして処理の負担を減らす * Reduce the processing burden by making the current drawing a Bitmap. */ - promises.push(this._$capture.execute()); + promises.push(captureExecute()); + + /** + * ローディング表示を起動 + * Launch loading display + */ + promises.push(loadingStart()); + + await Promise.all(promises); } - return Promise - .all(promises) - .then((): Promise => - { - /** - * 前の画面で取得したレスポンスデータを初期化 - * Initialize the response data obtained on the previous screen - */ - this - ._$removeResponse - .execute(this._$currentName); - - return Promise.resolve(); - }) - .then((): Promise => - { - /** - * 指定されたパス、もしくはURLからアクセス先を算出 - * Calculate the access point from the specified path or URL - */ - const object: QueryObject = this._$queryParser.execute(name); - - this._$currentName = object.name; - - /** - * 遷移履歴をセット - * Set transition history - */ - if (config.spa && !this._$popstate) { - history.pushState("", "", - `${location.origin}/${this._$currentName}${object.queryString}` - ); - } - - // update - this._$popstate = false; - - return Promise.resolve(); - }) - .then((): Promise[]> => - { - /** - * routing.jsonで設定したリクエスト処理を実行 - * Execute request processing set by routing.json - */ - return Promise - .all(this._$requestUseCase.execute(this._$currentName)); - }) - .then((responses): Promise => - { - /** - * レスポンス情報をマップに登録 - * Response information is registered on the map - */ - for (let idx: number = 0; idx < responses.length; ++idx) { - - const object: ResponseDTO = responses[idx]; - if (!object.name) { - continue; - } - - response.set(object.name, object.response); - } - - return Promise.resolve(); - }) - .then((): Promise => - { - /** - * ViewとViewModelを起動 - * Start View and ViewModel - */ - return Promise.resolve( - context.addChild(this._$currentName) - ); - }) - .then((view: View | void): Promise[]> => - { - /** - * コールバック設定があれば実行 - * Execute callback settings if any. - */ - const promises: Promise[] = []; - if (view && config.gotoView) { - promises.push(this._$callback.execute( - config.gotoView.callback, view - )); - } - - return Promise.all(promises); - }) - .then(() => - { - /** - * ローディング表示を終了 - * End loading display - */ - this._$loading.end(); - }); + /** + * 前の画面で取得したレスポンスデータを初期化 + * Initialize the response data obtained on the previous screen + */ + removeResponse(this._$currentName); + + /** + * 指定されたパス、もしくはURLからアクセス先を算出 + * Calculate the access point from the specified path or URL + */ + const queryObject: QueryObjectImpl = queryParser(name); + + /** + * 現在の画面名を更新 + * Update current screen name + */ + this._$currentName = queryObject.name; + + /** + * 遷移履歴をセット + * Set transition history + */ + if (config.spa && !this._$popstate) { + history.pushState("", "", + `${location.origin}/${this._$currentName}${queryObject.queryString}` + ); + } + + // update + this._$popstate = false; + + /** + * routing.jsonで設定したリクエスト処理を実行 + * Execute request processing set by routing.json + */ + const responses: ResponseDTO[] = await Promise.all(requestUseCase(this._$currentName)); + + /** + * レスポンス情報をマップに登録 + * Response information is registered on the map + */ + for (let idx: number = 0; idx < responses.length; ++idx) { + + const object: ResponseDTO = responses[idx]; + if (!object.name) { + continue; + } + + response.set(object.name, object.response); + } + + /** + * ViewとViewModelを起動 + * Start View and ViewModel + */ + const view: View = await context.boot(this._$currentName); + + /** + * コールバック設定があれば実行 + * Execute callback settings if any. + */ + if (view && config.gotoView) { + const promises: Promise[] = []; + promises.push(callback( + config.gotoView.callback, view + )); + + await Promise.all(promises); + } + + /** + * ローディング表示を終了 + * End loading display + */ + await loadingEnd(); + + /** + * 前の画面のキャプチャーを終了 + * End previous screen capture + */ + captureDispose(); } } diff --git a/src/application/Context.ts b/src/application/Context.ts index 7e7722e..1178e41 100644 --- a/src/application/Context.ts +++ b/src/application/Context.ts @@ -1,14 +1,15 @@ -import { ToCamelCase } from "../domain/convert/ToCamelCase"; +import { execute as toCamelCase } from "../domain/convert/ToCamelCase"; import { packages } from "./variable/Packages"; import type { View } from "../view/View"; import type { ViewModel } from "../view/ViewModel"; import type { Sprite } from "@next2d/display"; /** - * メインコンテキスト、ViewとViewModelのunbind、bindをコントロールします。 - * Controls unbind and bind of the main context, View and ViewModel. + * @description メインコンテキスト、ViewとViewModelのunbind、bindをコントロールします。 + * Controls unbind and bind of the main context, View and ViewModel. + * * @class - * @memberof application + * @memberof context */ export class Context { @@ -16,7 +17,6 @@ export class Context private _$viewModel: ViewModel | null; private _$viewName: string; private readonly _$root: Sprite; - private readonly _$toCamelCase: ToCamelCase; /** * @param {Sprite} root @@ -49,16 +49,9 @@ export class Context /** * @type {Sprite} - * @default null * @private */ this._$root = root; - - /** - * @type {ToCamelCase} - * @private - */ - this._$toCamelCase = new ToCamelCase(); } /** @@ -125,55 +118,57 @@ export class Context * @method * @public */ - addChild (name: string): Promise + async boot (name: string): Promise { - this._$viewName = this._$toCamelCase.execute(name); + this._$viewName = toCamelCase(name); - const viewName: string = `${this._$viewName}View`; + const viewName: string = `${this._$viewName}View`; const viewModelName: string = `${viewName}Model`; if (!packages.size || !packages.has(viewName) || !packages.has(viewModelName) ) { - return Promise.resolve(); + throw new Error("not found view or viewMode."); } - const PrevView: View | null = this._$view; - const PrevViewModel: ViewModel | null = this._$viewModel; + /** + * 現在のページをstageから削除して、unbind関数を実行 + * Delete current page from stage and execute unbind function + */ + if (this._$view) { + if (this._$viewModel) { + this._$viewModel.unbind(this._$view); + } + + // remove + if (this._$view.parent === this._$root) { + this._$root.removeChild(this._$view); + } + } + /** + * 遷移先のViewとViewModelを準備 + * Prepare the destination View and ViewModel + */ const ViewModelClass: typeof ViewModel = packages.get(viewModelName); this._$viewModel = new ViewModelClass(); const ViewClass: typeof View = packages.get(viewName); this._$view = new ViewClass(); - return Promise - .all([this._$viewModel.bind(this._$view)]) - .then(() => - { - if (!this._$view) { - return ; - } - - const root: Sprite | null = this._$root; - if (!root) { - throw new Error("the root is null."); - } - - root.addChild(this._$view); - - while (root.numChildren > 1) { - root.removeChild(root.getChildAt(0)); - } - - root.mouseChildren = true; + /** + * ViewModelにViewをbindしてページを生成 + * Bind a View to a ViewModel to generate a page + */ + await Promise.all([this._$viewModel.bind(this._$view)]); - if (PrevViewModel && PrevView) { - PrevViewModel.unbind(PrevView); - } + /** + * stageの一番背面にviewをセット + * Set the view at the very back of the stage + */ + this._$root.addChildAt(this._$view, 0); - return this._$view; - }); + return this._$view; } } \ No newline at end of file diff --git a/src/application/content/ContentBuilder.ts b/src/application/content/ContentBuilder.ts index 026aaa6..660e2d4 100644 --- a/src/application/content/ContentBuilder.ts +++ b/src/application/content/ContentBuilder.ts @@ -12,39 +12,30 @@ import type { * @class * @memberof application.content */ -export class ContentBuilder +export const execute = (instance: DisplayObjectImpl): void => { - /** - * @param {object} instance - * @return {object} - * @method - * @static - */ - static execute (instance: DisplayObjectImpl): void - { - const name = instance.namespace; - if (!loaderInfoMap.has(name)) { - return ; - } - - // Set the target LoaderInfo class - const loaderInfo: LoaderInfo | void = loaderInfoMap.get(name); - if (!loaderInfo || !loaderInfo._$data) { - return ; - } + const name = instance.namespace; + if (!loaderInfoMap.has(name)) { + return ; + } - const characterId: number | void = loaderInfo._$data.symbols.get(name); - if (!characterId) { - return ; - } + // Set the target LoaderInfo class + const loaderInfo: LoaderInfo | void = loaderInfoMap.get(name); + if (!loaderInfo || !loaderInfo._$data) { + return ; + } - const character: Character = loaderInfo._$data.characters[characterId]; - if (!character) { - return ; - } + const characterId: number | void = loaderInfo._$data.symbols.get(name); + if (!characterId) { + return ; + } - instance._$loaderInfo = loaderInfo; - instance._$characterId = characterId; - instance._$sync(character); + const character: Character = loaderInfo._$data.characters[characterId]; + if (!character) { + return ; } -} + + instance._$loaderInfo = loaderInfo; + instance._$characterId = characterId; + instance._$sync(character); +}; \ No newline at end of file diff --git a/src/application/content/MovieClipContent.ts b/src/application/content/MovieClipContent.ts index b22c24a..540e328 100644 --- a/src/application/content/MovieClipContent.ts +++ b/src/application/content/MovieClipContent.ts @@ -1,5 +1,5 @@ import { MovieClip } from "@next2d/display"; -import { ContentBuilder } from "./ContentBuilder"; +import { execute as contentBuilder } from "./ContentBuilder"; /** * @description NoCode Toolで作成したMovieClipの動的生成の補完を行うクラス。 @@ -19,7 +19,7 @@ export class MovieClipContent extends MovieClip { super(); - ContentBuilder.execute(this); + contentBuilder(this); // initial processing this.initialize(); diff --git a/src/application/content/ShapeContent.ts b/src/application/content/ShapeContent.ts index f8d003e..aca96a9 100644 --- a/src/application/content/ShapeContent.ts +++ b/src/application/content/ShapeContent.ts @@ -1,5 +1,5 @@ import { Shape } from "@next2d/display"; -import { ContentBuilder } from "./ContentBuilder"; +import { execute as contentBuilder } from "./ContentBuilder"; /** * @description NoCode Toolで作成したShapeの動的生成の補完を行うクラス。 @@ -19,7 +19,7 @@ export class ShapeContent extends Shape { super(); - ContentBuilder.execute(this); + contentBuilder(this); // initial processing this.initialize(); diff --git a/src/application/content/TextFieldContent.ts b/src/application/content/TextFieldContent.ts index 1fa8c81..b20e702 100644 --- a/src/application/content/TextFieldContent.ts +++ b/src/application/content/TextFieldContent.ts @@ -1,5 +1,5 @@ import { TextField } from "@next2d/display"; -import { ContentBuilder } from "./ContentBuilder"; +import { execute as contentBuilder } from "./ContentBuilder"; /** * @description NoCode Toolで作成したTextFieldの動的生成の補完を行うクラス。 @@ -19,7 +19,7 @@ export class TextFieldContent extends TextField { super(); - ContentBuilder.execute(this); + contentBuilder(this); // initial processing this.initialize(); diff --git a/src/application/content/VideoContent.ts b/src/application/content/VideoContent.ts index a47d8a6..52b6d79 100644 --- a/src/application/content/VideoContent.ts +++ b/src/application/content/VideoContent.ts @@ -1,5 +1,5 @@ import { Video } from "@next2d/media"; -import { ContentBuilder } from "./ContentBuilder"; +import { execute as contentBuilder } from "./ContentBuilder"; /** * @description NoCode Toolで作成したVideoの動的生成の補完を行うクラス。 @@ -19,7 +19,7 @@ export class VideoContent extends Video { super(); - ContentBuilder.execute(this); + contentBuilder(this); // initial processing this.initialize(); diff --git a/src/application/service/RemoveResponse.ts b/src/application/service/RemoveResponse.ts index 565442b..132a6b6 100644 --- a/src/application/service/RemoveResponse.ts +++ b/src/application/service/RemoveResponse.ts @@ -1,85 +1,55 @@ -import { RequestType } from "../../infrastructure/constant/RequestType"; -import { RequestParser } from "../../domain/parser/RequestParser"; +import { execute as requestParser } from "../../domain/parser/RequestParser"; import { loaderInfoMap } from "../variable/LoaderInfoMap"; import { response } from "../variable/Response"; import type { LoaderInfo } from "@next2d/display"; import type { ParentImpl } from "@next2d/interface"; - -interface Object { - type: string; - name: string; - path: string; - cache?: boolean; - class?: string; - access?: string; - method?: string; - callback?: string | string[]; -} +import { RequestImpl } from "src/interface/RequestImpl"; /** - * @class - * @memberof application.service + * @param {string} name + * @return {void} + * @method + * @public */ -export class RemoveResponse +export const execute = (name: string): void => { - private readonly _$requestParser: RequestParser; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {RequestParser} - * @private - */ - this._$requestParser = new RequestParser(); - } - - /** - * @param {string} name - * @return {void} - * @method - * @public - */ - execute (name: string): void - { - const requests: Object[] = this._$requestParser.execute(name); - for (let idx: number = 0; idx < requests.length; ++idx) { - - const object: Object = requests[idx]; + const requests: RequestImpl[] = requestParser(name); + for (let idx: number = 0; idx < requests.length; ++idx) { - if (object.type !== RequestType.CONTENT) { - continue; - } + const object: RequestImpl = requests[idx]; - if (object.cache || !response.has(object.name)) { - continue; - } + if (object.type !== "content") { + continue; + } - /** - * キャッシュしないパッケージはインメモリから削除 - * Remove non-cached packages from in-memory - */ - const content: ParentImpl = response.get(object.name); - const contentLoaderInfo: LoaderInfo | null = content._$loaderInfo; - if (contentLoaderInfo && contentLoaderInfo._$data) { - const symbols: Map = contentLoaderInfo._$data.symbols; - if (symbols.size) { - for (const name of symbols.keys()) { - loaderInfoMap.delete(name); - } - } - } + if (object.cache + || !object.name + || !response.has(object.name) + ) { + continue; } /** - * レスポンスデータをマップに格納 - * Stores response data in a map + * キャッシュしないパッケージはインメモリから削除 + * Remove non-cached packages from in-memory */ - if (response.size) { - response.clear(); + const content: ParentImpl = response.get(object.name); + const contentLoaderInfo: LoaderInfo | null = content._$loaderInfo; + if (contentLoaderInfo && contentLoaderInfo._$data) { + const symbols: Map = contentLoaderInfo._$data.symbols; + if (symbols.size) { + for (const name of symbols.keys()) { + loaderInfoMap.delete(name); + } + } } } -} \ No newline at end of file + + /** + * レスポンスデータを初期化 + * Initialize response data + */ + if (response.size) { + response.clear(); + } +}; \ No newline at end of file diff --git a/src/application/variable/Context.ts b/src/application/variable/Context.ts index 14606da..6bf6295 100644 --- a/src/application/variable/Context.ts +++ b/src/application/variable/Context.ts @@ -14,20 +14,16 @@ export let context: Context; * @method * @private */ -export const $createContext = (config: ConfigImpl): Promise => +export const $createContext = async (config: ConfigImpl): Promise => { - return window + const root: Sprite = await window .next2d .createRootMovieClip( config.stage.width, config.stage.height, config.stage.fps, config.stage.options - ) - .then((root: Sprite): Promise => - { - context = new Context(root); + ); - return Promise.resolve(); - }); + context = new Context(root); }; \ No newline at end of file diff --git a/src/application/variable/Parser.ts b/src/application/variable/Parser.ts deleted file mode 100644 index 5fd9955..0000000 --- a/src/application/variable/Parser.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ConfigParser } from "../../domain/parser/ConfigParser"; - -export const parser: ConfigParser = new ConfigParser(); \ No newline at end of file diff --git a/src/domain/callback/Callback.test.ts b/src/domain/callback/Callback.test.ts new file mode 100644 index 0000000..ba6982f --- /dev/null +++ b/src/domain/callback/Callback.test.ts @@ -0,0 +1,80 @@ +import { execute } from "./Callback"; +import { packages } from "../../application/variable/Packages"; + +describe("CallbackTest", () => +{ + test("execute test case1", () => + { + execute() + .then((result) => + { + expect(result).toBe(undefined); + }); + }); + + test("execute single test", () => + { + // mock + const SingleTest = class SingleTest + { + execute (value: any): any + { + return value; + } + }; + + packages.clear(); + packages.set("SingleTest", SingleTest); + + execute("SingleTest", "single test") + .then((results: string[] | void) => + { + if (!results) { + throw new Error("stop test"); + } + + expect(results.length).toBe(1); + const result: string = results[0]; + expect(result).toBe("single test"); + }); + }); + + test("execute multiple test", () => + { + // mock + const MultipleTestCase1 = class MultipleTest + { + execute (value: any): any + { + return `${value}_1`; + } + }; + + const MultipleTestCase2 = class MultipleTest + { + execute (value: any): any + { + return `${value}_2`; + } + }; + + packages.clear(); + packages.set("multiple.test.case1", MultipleTestCase1); + packages.set("multiple.test.case2", MultipleTestCase2); + + execute(["multiple.test.case1", "multiple.test.case2"], "multiple test") + .then((results: string[] | void) => + { + if (!results) { + throw new Error("stop test"); + } + + expect(results.length).toBe(2); + const result1: string = results[0]; + expect(result1).toBe("multiple test_1"); + + const result2: string = results[1]; + expect(result2).toBe("multiple test_2"); + }); + }); +}); \ No newline at end of file diff --git a/src/domain/callback/Callback.ts b/src/domain/callback/Callback.ts index fcba224..f529cb6 100644 --- a/src/domain/callback/Callback.ts +++ b/src/domain/callback/Callback.ts @@ -1,45 +1,39 @@ import { packages } from "../../application/variable/Packages"; -import { parser } from "../../application/variable/Parser"; /** - * @class - * @memberof domain.callback + * @description configで指定されたクラスのexecute関数を実行 + * Execute function of the class specified in config. + * + * @param {string|array} [callback=""] + * @param {*} [value=null] + * @return {Promise} + * @method + * @public */ -export class Callback -{ - /** - * @description configで指定されたクラスのexecute関数を実行 - * Execute function of the class specified in config. - * - * @param {string|array} [callback=""] - * @param {*} [value=null] - * @return {Promise} - * @method - * @public - */ - execute (callback: string | string[] = "", value: any = null): Promise[]|void> - { - if (!callback) { - return Promise.resolve(); - } - - const callbacks: string[] = typeof callback === "string" - ? [callback] - : callback; +export const execute = ( + callback: string | string[] = "", + value: any = null +): Promise[]|void> => { - const promises: Promise[] = []; + if (!callback) { + return Promise.resolve(); + } - for (let idx: number = 0; idx < callbacks.length; ++idx) { + const callbacks: string[] = typeof callback === "string" + ? [callback] + : callback; - const name: string = parser.execute(callbacks[idx]); - if (!packages.has(name)) { - continue; - } + const promises: Promise[] = []; + for (let idx: number = 0; idx < callbacks.length; ++idx) { - const CallbackClass: any = packages.get(name); - promises.push(new CallbackClass().execute(value)); + const name: string = callbacks[idx]; + if (!packages.has(name)) { + continue; } - return Promise.all(promises); + const CallbackClass: any = packages.get(name); + promises.push(new CallbackClass().execute(value)); } -} \ No newline at end of file + + return Promise.all(promises); +}; \ No newline at end of file diff --git a/src/domain/convert/ToCamelCase.test.ts b/src/domain/convert/ToCamelCase.test.ts new file mode 100644 index 0000000..91dddbf --- /dev/null +++ b/src/domain/convert/ToCamelCase.test.ts @@ -0,0 +1,19 @@ +import { execute } from "./ToCamelCase"; + +describe("ToCamelCaseTest", () => +{ + test("execute test case1", () => + { + expect(execute("home")).toBe("Home"); + }); + + test("execute test case2", () => + { + expect(execute("quest/list")).toBe("QuestList"); + }); + + test("execute test case3", () => + { + expect(execute("game/list/page")).toBe("GameListPage"); + }); +}); \ No newline at end of file diff --git a/src/domain/convert/ToCamelCase.ts b/src/domain/convert/ToCamelCase.ts index d7eb091..fbc423f 100644 --- a/src/domain/convert/ToCamelCase.ts +++ b/src/domain/convert/ToCamelCase.ts @@ -1,30 +1,23 @@ /** - * @class - * @memberof domain.convert + * @description キャメルケースに変換 + * Convert to CamelCase + * + * @param {string} name + * @return {string} + * @method + * @public */ -export class ToCamelCase +export const execute = (name: string): string => { - /** - * @description キャメルケースに変換 - * Convert to CamelCase - * - * @param {string} name - * @return {string} - * @method - * @public - */ - execute (name: string): string - { - const names: string[] = name.split("/"); + const names: string[] = name.split("/"); - let viewName: string = ""; - for (let idx: number = 0; names.length > idx; ++idx) { - name = names[idx]; - viewName += name - .charAt(0) - .toUpperCase() + name.slice(1); - } - - return viewName; + let viewName: string = ""; + for (let idx: number = 0; names.length > idx; ++idx) { + name = names[idx]; + viewName += name + .charAt(0) + .toUpperCase() + name.slice(1); } -} \ No newline at end of file + + return viewName; +}; \ No newline at end of file diff --git a/src/domain/loading/Loading.ts b/src/domain/loading/Loading.ts index a0870b8..3fbda7c 100644 --- a/src/domain/loading/Loading.ts +++ b/src/domain/loading/Loading.ts @@ -1,67 +1,91 @@ +import type { LoadingImpl } from "src/interface/LoadingImpl"; import { config } from "../../application/variable/Config"; import { packages } from "../../application/variable/Packages"; -import { parser } from "../../application/variable/Parser"; import { DefaultLoading } from "../screen/DefaultLoading"; /** - * @class - * @memberof domain.loading + * @type {object} + * @default null + * @private */ -export class Loading +let $instance: LoadingImpl | null = null; + +/** + * @description ページ遷移時のローディング演出を開始 + * Starts loading performance at page transitions + * + * @return {Promise} + * @method + * @public + */ +export const start = (): Promise => { - /** - * @description デフォルトのローディング演出を開始 - * Starts default loading direction - * - * @return {void} - * @method - * @public - */ - start (): void + return new Promise((resolve): void => { if (!config || !config.loading) { - return ; + return resolve(); } - const callback: string | void = config.loading.callback; - if (!callback) { - return ; + const name: string | void = config.loading.callback; + if (!name) { + return resolve(); } - const name: string = parser.execute(callback); + if (!$instance) { + const CallbackClass: any = packages.has(name) + ? packages.get(name) + : DefaultLoading; + + $instance = new CallbackClass(); + } + + if (!$instance) { + return resolve(); + } - const CallbackClass: any = packages.has(name) - ? packages.get(name) - : DefaultLoading; + $instance.start(); - new CallbackClass().start(); - } + setTimeout(() => + { + resolve(); + }, 500); + }); +}; - /** - * @description デフォルトのローディング演出を終了 - * End default loading direction - * - * @return {void} - * @method - * @public - */ - end (): void +/** + * @description ページ遷移時のローディング演出を終了 + * Terminate loading direction at page transition + * + * @return {Promise} + * @method + * @public + */ +export const end = (): Promise => +{ + return new Promise((resolve): void => { if (!config || !config.loading) { - return ; + return resolve(); } - const callback: string|undefined = config.loading.callback; - if (!callback) { - return ; + const name: string | undefined = config.loading.callback; + if (!name) { + return resolve(); } - const name: string = parser.execute(callback); + if (!$instance) { + + const CallbackClass: any = packages.has(name) + ? packages.get(name) + : DefaultLoading; - const CallbackClass: any = packages.has(name) - ? packages.get(name) - : DefaultLoading; + $instance = new CallbackClass(); + } + + if (!$instance) { + return resolve(); + } - new CallbackClass().end(); - } -} \ No newline at end of file + resolve($instance.end()); + }); +}; \ No newline at end of file diff --git a/src/domain/parser/ConfigParser.ts b/src/domain/parser/ConfigParser.ts deleted file mode 100644 index 1bf91b2..0000000 --- a/src/domain/parser/ConfigParser.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { config } from "../../application/variable/Config"; - -/** - * configファイルに設定した変数パスのパーサークラス - * Parser class for variable paths set in config file - * - * @class - * @memberof domain.parser - */ -export class ConfigParser -{ - /** - * @description 指定されたstringを分解して、configの変数パスの値を返す - * Disassembles the given string and returns the value of the config variable path - * - * @param {string} [value=""] - * @return {string} - * @method - * @public - */ - execute (value: string = ""): string - { - if (value.indexOf("{{") === -1) { - return value; - } - - const regexp = new RegExp(/{{(.*?)}}/, "g"); - const values: RegExpMatchArray | null = value.match(regexp); - if (!values) { - return value; - } - - let returnValue: string = value; - for (let idx: number = 0; idx < values.length; ++idx) { - - const value: string = values[idx]; - - const names: string[] = value - .replace(/\{|\{|\}|\}/g, "") - .replace(/ /g, "") - .split("."); - - if (!names.length) { - continue; - } - - let configValue: any = config; - for (let idx: number = 0; idx < names.length; ++idx) { - const name: string = names[idx]; - if (name in configValue) { - configValue = configValue[name]; - } - } - - if (config === configValue) { - continue; - } - - returnValue = returnValue.replace(value, configValue); - } - - return returnValue; - } -} \ No newline at end of file diff --git a/src/domain/parser/QueryParser.test.ts b/src/domain/parser/QueryParser.test.ts new file mode 100644 index 0000000..855b5b0 --- /dev/null +++ b/src/domain/parser/QueryParser.test.ts @@ -0,0 +1,251 @@ +import { execute } from "./QueryParser"; +import { query } from "../../application/variable/Query"; +import { $setConfig } from "../../application/variable/Config"; +import { QueryObjectImpl } from "../../interface/QueryObjectImpl"; +import { vi } from "vitest"; + +Object.defineProperty(window, "location", { + "get": vi.fn().mockReturnValue({ + "search": "", + "pathname": "" + }) +}); + +describe("QueryParserTest", () => +{ + test("parse query test case1", () => + { + query.clear(); + query.set("test", 123); + expect(query.size).toBe(1); + + const object: QueryObjectImpl = execute(); + + expect(query.size).toBe(0); + expect(object.name).toBe("top"); + expect(object.queryString).toBe(""); + }); + + test("parse query test case2", () => + { + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute("@test"); + + expect(query.size).toBe(0); + expect(object.name).toBe("test"); + expect(object.queryString).toBe(""); + }); + + test("parse location.search test case1", () => + { + // @ts-ignore + globalThis.location.search = "?q=abc&sample=1"; + + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute(""); + + expect(query.size).toBe(2); + expect(query.get("q")).toBe("abc"); + expect(query.get("sample")).toBe("1"); + expect(object.name).toBe("top"); + expect(object.queryString).toBe("?q=abc&sample=1"); + }); + + test("parse location.pathname un match test", () => + { + // mock + const config = { + "platform": "web", + "spa": true, + "stage": { + "width": 240, + "height": 240, + "fps": 12, + "options": {} + }, + "routing": { + "top": {} + } + }; + + $setConfig(config); + + // @ts-ignore + globalThis.location.pathname = "/quest/list"; + + // @ts-ignore + globalThis.location.search = "?q=xyz&sample=0"; + + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute(""); + + expect(query.size).toBe(2); + expect(query.get("q")).toBe("xyz"); + expect(query.get("sample")).toBe("0"); + expect(object.name).toBe("top"); + expect(object.queryString).toBe("?q=xyz&sample=0"); + }); + + test("parse location.pathname public test", () => + { + // mock + const config = { + "platform": "web", + "spa": true, + "stage": { + "width": 240, + "height": 240, + "fps": 12, + "options": {} + }, + "routing": { + "quest/list": { + "private": false + } + } + }; + + $setConfig(config); + + // @ts-ignore + globalThis.location.pathname = "/quest/list"; + + // @ts-ignore + globalThis.location.search = "?q=xyz&sample=0"; + + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute(""); + + expect(query.size).toBe(2); + expect(query.get("q")).toBe("xyz"); + expect(query.get("sample")).toBe("0"); + expect(object.name).toBe("quest/list"); + expect(object.queryString).toBe("?q=xyz&sample=0"); + }); + + test("parse location.pathname private test", () => + { + // mock + const config = { + "platform": "web", + "spa": true, + "stage": { + "width": 240, + "height": 240, + "fps": 12, + "options": {} + }, + "routing": { + "quest/list": { + "private": true + } + } + }; + + $setConfig(config); + + // @ts-ignore + globalThis.location.pathname = "/quest/list"; + + // @ts-ignore + globalThis.location.search = "?q=xyz&sample=0"; + + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute(""); + + expect(query.size).toBe(2); + expect(query.get("q")).toBe("xyz"); + expect(query.get("sample")).toBe("0"); + expect(object.name).toBe("top"); + expect(object.queryString).toBe("?q=xyz&sample=0"); + }); + + test("parse location.pathname redirect test", () => + { + // mock + const config = { + "platform": "web", + "spa": true, + "stage": { + "width": 240, + "height": 240, + "fps": 12, + "options": {} + }, + "routing": { + "quest/list": { + "private": true, + "redirect": "quest/detail" + } + } + }; + + $setConfig(config); + + // @ts-ignore + globalThis.location.pathname = "/quest/list"; + + // @ts-ignore + globalThis.location.search = "?q=xyz&sample=0"; + + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute(""); + + expect(query.size).toBe(2); + expect(query.get("q")).toBe("xyz"); + expect(query.get("sample")).toBe("0"); + expect(object.name).toBe("quest/detail"); + expect(object.queryString).toBe("?q=xyz&sample=0"); + }); + + test("parse name query test", () => + { + // mock + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute("page/test?abc=123&xyz=999"); + + expect(query.size).toBe(2); + expect(query.get("abc")).toBe("123"); + expect(query.get("xyz")).toBe("999"); + expect(object.name).toBe("page/test"); + expect(object.queryString).toBe("?abc=123&xyz=999"); + }); + + test("parse name path test case1", () => + { + // mock + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute("./test"); + + expect(query.size).toBe(0); + expect(object.name).toBe("test"); + }); + + test("parse name path test case2", () => + { + // mock + query.clear(); + expect(query.size).toBe(0); + + const object: QueryObjectImpl = execute("./"); + + expect(query.size).toBe(0); + expect(object.name).toBe("top"); + }); +}); \ No newline at end of file diff --git a/src/domain/parser/QueryParser.ts b/src/domain/parser/QueryParser.ts index b21a560..5158ae2 100644 --- a/src/domain/parser/QueryParser.ts +++ b/src/domain/parser/QueryParser.ts @@ -1,95 +1,89 @@ +import type { QueryObjectImpl } from "src/interface/QueryObjectImpl"; +import type { RoutingImpl } from "src/interface/RoutingImpl"; import { config } from "../../application/variable/Config"; import { query } from "../../application/variable/Query"; -interface Object { - name: string; - queryString: string; -} - /** - * @class - * @memberof domain.parser + * @description 指定されたQueryStringか、URLのQueryStringをquery mapに登録 + * Register the specified QueryString or URL QueryString in the query map + * + * @param {string} [name=""] + * @return {object} + * @method + * @public */ -export class QueryParser +export const execute = (name: string = ""): QueryObjectImpl => { /** - * @description 指定されたQueryStringか、URLのQueryStringをquery mapに登録 - * Register the specified QueryString or URL QueryString in the query map - * - * @param {string} [name=""] - * @return {object} - * @method - * @public + * 前のシーンのクエリデータを初期化 + * Initialize query data from previous scene */ - execute (name: string = ""): Object - { - if (query.size) { - query.clear(); - } + if (query.size) { + query.clear(); + } - /** - * QueryStringがあれば分解 - * Disassemble QueryString if available - */ - let queryString: string = ""; - if (!name && location.search) { - queryString = location.search; - const parameters = queryString.slice(1).split("&"); - for (let idx = 0; idx < parameters.length; ++idx) { - const pair: string[] = parameters[idx].split("="); - query.set(pair[0], pair[1]); - } + /** + * QueryStringがあれば分解 + * Disassemble QueryString if available + */ + let queryString: string = ""; + if (!name && location.search) { + queryString = location.search; + const parameters = queryString.slice(1).split("&"); + for (let idx: number = 0; idx < parameters.length; ++idx) { + const pair: string[] = parameters[idx].split("="); + query.set(pair[0], pair[1]); } + } - if (!name) { - const names: string[] = location.pathname.split("/"); - names.shift(); - name = `${names.join("/")}`; - if (name && config && config.routing) { - const routing: any = config.routing[name]; - if (!routing) { - name = "top"; - } - - if (routing && routing.private) { - name = routing.redirect || "top"; - } + if (!name) { + const names: string[] = location.pathname.split("/"); + names.shift(); + name = `${names.join("/")}`; + if (name && config && config.routing) { + const routing: RoutingImpl = config.routing[name]; + if (!routing) { + name = "top"; } - if (!name) { - name = "top"; + if (routing && routing.private) { + name = routing.redirect || "top"; } } - /** - * 任意で設定したQueryStringを分解 - * Decompose an arbitrarily set QueryString - */ - if (name.indexOf("?") > -1) { + if (!name) { + name = "top"; + } + } - const names: string[] = name.split("?"); + /** + * 任意で設定したQueryStringを分解 + * Decompose an arbitrarily set QueryString + */ + if (name.indexOf("?") > -1) { - name = names[0]; - queryString = `?${names[1]}`; + const names: string[] = name.split("?"); - const parameters: string[] = names[1].split("&"); - for (let idx = 0; idx < parameters.length; ++idx) { - const pair: string[] = parameters[idx].split("="); - query.set(pair[0], pair[1]); - } - } + name = names[0]; + queryString = `?${names[1]}`; - if (name.slice(0, 1) === ".") { - name = name.split("/").slice(1).join("/") || "top"; + const parameters: string[] = names[1].split("&"); + for (let idx: number = 0; idx < parameters.length; ++idx) { + const pair: string[] = parameters[idx].split("="); + query.set(pair[0], pair[1]); } + } - if (name.indexOf("@") > -1) { - name = name.replace("@", ""); - } + if (name.slice(0, 1) === ".") { + name = name.split("/").slice(1).join("/") || "top"; + } - return { - "name": name, - "queryString": queryString - }; + if (name.indexOf("@") > -1) { + name = name.replace("@", ""); } -} \ No newline at end of file + + return { + "name": name, + "queryString": queryString + }; +}; \ No newline at end of file diff --git a/src/domain/parser/RequestParser.test.ts b/src/domain/parser/RequestParser.test.ts new file mode 100644 index 0000000..8b477db --- /dev/null +++ b/src/domain/parser/RequestParser.test.ts @@ -0,0 +1,138 @@ +import { execute } from "./RequestParser"; +import { $setConfig } from "../../../src/application/variable/Config"; + +describe("RequestParserTest", () => +{ + test("request parse no match test case1", () => + { + // mock + const config = { + "platform": "web", + "spa": true, + "stage": { + "width": 240, + "height": 240, + "fps": 12, + "options": {} + }, + "routing": {} + }; + + $setConfig(config); + + const requests: Object[] = execute("top"); + expect(requests.length).toBe(0); + }); + + test("request parse no match test case2", () => + { + // mock + const config = { + "platform": "web", + "spa": true, + "stage": { + "width": 240, + "height": 240, + "fps": 12, + "options": {} + }, + "routing": { + "top": {} + } + }; + + $setConfig(config); + + const requests: Object[] = execute("top"); + expect(requests.length).toBe(0); + }); + + test("request parse match test case1", () => + { + // mock + const config = { + "platform": "web", + "spa": true, + "stage": { + "width": 240, + "height": 240, + "fps": 12, + "options": {} + }, + "routing": { + "top": { + "requests": [ + { + "type": "json", + "name": "TopTest", + "path": "local" + } + ] + } + } + }; + + $setConfig(config); + + const requests: Object[] = execute("top"); + expect(requests.length).toBe(1); + + const object: Object = requests[0]; + expect(object.type).toBe("json"); + expect(object.name).toBe("TopTest"); + expect(object.path).toBe("local"); + }); + + test("request parse cluster test case1", () => + { + // mock + const config = { + "platform": "web", + "spa": true, + "stage": { + "width": 240, + "height": 240, + "fps": 12, + "options": {} + }, + "routing": { + "@sample": { + "requests": [ + { + "type": "content", + "path": "{{ content.endPoint }}content/sample.json", + "name": "MainContent", + "cache": true + } + ] + }, + "top": { + "requests": [ + { + "type": "cluster", + "path": "@sample" + }, + { + "type": "json", + "path": "{{ api.endPoint }}api/top.json", + "name": "TopText" + } + ] + } + } + }; + + $setConfig(config); + + const requests: Object[] = execute("top"); + expect(requests.length).toBe(2); + + const object1: Object = requests[0]; + expect(object1.type).toBe("content"); + expect(object1.name).toBe("MainContent"); + + const object2: Object = requests[1]; + expect(object2.type).toBe("json"); + expect(object2.name).toBe("TopText"); + }); +}); \ No newline at end of file diff --git a/src/domain/parser/RequestParser.ts b/src/domain/parser/RequestParser.ts index 8d1df93..f48a64c 100644 --- a/src/domain/parser/RequestParser.ts +++ b/src/domain/parser/RequestParser.ts @@ -1,62 +1,51 @@ -import { RequestType } from "../../infrastructure/constant/RequestType"; +import { RequestImpl } from "src/interface/RequestImpl"; import { config } from "../../application/variable/Config"; -import type { RequestTypeImpl } from "../../interface/RequestTypeImpl"; - -interface Object { - type: RequestTypeImpl; - name: string; - path: string; - cache: boolean; - class: string; - access: string; - method: string; - callback?: string | string[]; -} +import type { RoutingImpl } from "src/interface/RoutingImpl"; /** - * @class - * @memberof domain.parser + * @description routing.jsonに設定されたrequestsを返却します。 + * クラスターの指定があった場合は返却する配列にマージして返却 + * Returns requests set in routing.json. + * If a cluster is specified, it is merged into the array to be returned + * + * @param {string} name + * @return {array} + * @method + * @public */ -export class RequestParser +export const execute = (name: string): RequestImpl[] => { - /** - * @description routing.jsonに設定されたrequestsを返却します。 - * クラスターの指定があった場合は返却する配列にマージして返却 - * Returns requests set in routing.json. - * If a cluster is specified, it is merged into the array to be returned - * - * @param {string} name - * @return {array} - * @method - * @public - */ - execute (name: string): Object[] - { - if (!config || !config.routing) { - return []; - } + const requests: RequestImpl[] = []; - const routing: any = config.routing[name]; - if (!routing || !routing.requests) { - return []; - } + if (!config || !config.routing) { + return requests; + } + + const routing: RoutingImpl = config.routing[name]; + if (!routing || !routing.requests) { + return requests; + } - const requests: Object[] = []; - for (let idx = 0; idx < routing.requests.length; idx++) { + for (let idx: number = 0; idx < routing.requests.length; idx++) { - const object: Object = routing.requests[idx]; + const request: RequestImpl = routing.requests[idx]; - if (object.type !== RequestType.CLUSTER) { - requests.push(object); - continue; - } + if (request.type !== "cluster") { + requests.push(request); + continue; + } - const results: Object[] = new RequestParser().execute(object.path); - for (let idx = 0; idx < results.length; ++idx) { - requests.push(results[idx]); - } + if (!request.path) { + continue; } - return requests; + /** + * クラスターの場合は分解して配列に追加 + * For clusters, disassemble and add to array + */ + const results: RequestImpl[] = execute(request.path); + requests.push(...results); } -} \ No newline at end of file + + return requests; +}; \ No newline at end of file diff --git a/src/domain/screen/Capture.ts b/src/domain/screen/Capture.ts index 5066aae..e77e043 100644 --- a/src/domain/screen/Capture.ts +++ b/src/domain/screen/Capture.ts @@ -4,60 +4,103 @@ import { $currentPlayer } from "@next2d/util"; import { Shape } from "@next2d/display"; import type { Sprite } from "@next2d/display"; import type { Player } from "@next2d/core"; + +/** + * @type {Shape} + * @private + */ +const shape: Shape = new Shape(); + /** - * @class - * @memberof domain.screen + * @type {number} + * @default 0 + * @private */ -export class Capture +let cacheX: number = 0; + +/** + * @type {number} + * @default 0 + * @private + */ +let cacheY: number = 0; + +/** + * @description 現時点の描画キャプチャーを生成 + * Generate current drawing capture + * + * @return {Promise} + * @method + * @public + */ +export const execute = (): Promise => { - /** - * @description 現時点の描画キャプチャーを生成 - * Generate current drawing capture - * - * @return {Promise} - * @method - * @public - */ - execute (): Promise + return new Promise((resolve) => { - return new Promise((resolve) => - { - const width: number = config.stage.width; - const height: number = config.stage.height; - - const mask: any = new Shape(); - mask + const width: number = config.stage.width; + const height: number = config.stage.height; + if (shape.width !== width || shape.width !== height) { + shape .graphics + .clear() .beginFill(0, 0.8) .drawRect(0, 0, width, height) .endFill(); + } + + const player: Player = $currentPlayer(); + + const tx: number = player.x; + if (tx && cacheX !== tx) { + cacheX = tx; + const scaleX: number = player.scaleX; + shape.scaleX = (width + tx * 2 / scaleX) / width; + shape.x = -tx / scaleX; + } + + const ty: number = player.y; + if (ty && cacheY !== ty) { + cacheY = ty; + const scaleY: number = player.scaleY; + shape.scaleY = (height + ty * 2 / scaleY) / height; + shape.y = -ty / scaleY; + } + + const root: Sprite = context.root; + if (root) { + /** + * マウス操作を強制停止 + * Mouse operation is forced to stop + */ + root.mouseChildren = false; + root.addChild(shape); + } + + resolve(); + }); +}; + +/** + * @description 画面キャプチャーのShapeをStageから削除 + * Delete Screen Capture Shape from Stage + * + * @return {void} + * @method + * @public + */ +export const dispose = (): void => +{ + const root: Sprite = context.root; + if (root) { + + if (shape.parent === root) { + root.removeChild(shape); + } - const player: Player = $currentPlayer(); - - const tx: number = player.x; - if (tx) { - const scaleX: number = player.scaleX; - mask.scaleX = (width + tx * 2 / scaleX) / width; - mask.x = -tx / scaleX; - } - - const ty: number = player.y; - if (ty) { - const scaleY: number = player.scaleY; - mask.scaleY = (height + ty * 2 / scaleY) / height; - mask.y = -ty / scaleY; - } - - const root: Sprite = context.root; - if (root) { - root.mouseChildren = false; - root.addChild(mask); - } - - setTimeout(() => - { - resolve(); - }, 350); - }); + /** + * マウス操作を有効化 + * Enable Mouse Operation + */ + root.mouseChildren = true; } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/domain/screen/DefaultLoading.ts b/src/domain/screen/DefaultLoading.ts index 1c17f60..5feabda 100644 --- a/src/domain/screen/DefaultLoading.ts +++ b/src/domain/screen/DefaultLoading.ts @@ -1,5 +1,98 @@ -import { $currentPlayer } from "@next2d/util"; -import type { Player } from "@next2d/core"; +import { context } from "../../application/variable/Context"; +import { config } from "../../application/variable/Config"; +import { Sprite, Shape } from "@next2d/display"; +import { Tween, Job, Easing } from "@next2d/ui"; +import { Event } from "@next2d/events"; + +/** + * @type {Sprite} + * @private + */ +const $sprite: Sprite = new Sprite(); + +/** + * @return {object} + * @method + * @private + */ +const getStartObject = (): object => { + return { + "scaleX": 0.1, + "scaleY": 0.1, + "alpha": 0 + }; +}; + +/** + * @return {object} + * @method + * @private + */ +const getEndObject = (): object => { + return { + "scaleX": 1, + "scaleY": 1, + "alpha": 1 + }; +}; + +/** + * @description ローディングのアニメーションに必要なDisplayObjectを追加 + * Add DisplayObject needed for loading animation + * + * @return {void} + * @method + * @private + */ +const initialize = (): void => +{ + for (let idx: number = 0; idx < 3; ++idx) { + + const sprite: Sprite = new Sprite(); + sprite.addChild(new Shape()); + + const reduceJob: Job = Tween.add( + sprite, + getEndObject(), + getStartObject(), + 0.2, + 0.7, + Easing.inOutCubic + ); + sprite.setLocalVariable("reduceJob", reduceJob); + + const expandJob: Job = Tween.add( + sprite, + getStartObject(), + getEndObject(), + 0.2, + 0.7, + Easing.inOutCubic + ); + sprite.setLocalVariable("expandJob", expandJob); + + // loop event + reduceJob.addEventListener(Event.COMPLETE, () => + { + const expandJob: Job = sprite.getLocalVariable("expandJob"); + expandJob.from = getStartObject(); + expandJob.to = getEndObject(); + expandJob.start(); + }); + + // loop event + expandJob.addEventListener(Event.COMPLETE, () => + { + const reduceJob: Job = sprite.getLocalVariable("reduceJob"); + reduceJob.from = getEndObject(); + reduceJob.to = getStartObject(); + reduceJob.start(); + }); + + $sprite.addChild(sprite); + } +}; +initialize(); /** * @class @@ -7,21 +100,6 @@ import type { Player } from "@next2d/core"; */ export class DefaultLoading { - private readonly _$elementId: string; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {string} - * @private - */ - this._$elementId = "__next2d__framework_loading"; - } - /** * @description Canvasが設置されたDOMにローディング演出を登録、既にDOMがあれば演出を表示 * Register loading direction in the DOM where Canvas is installed, @@ -33,82 +111,56 @@ export class DefaultLoading */ start (): void { - const element: HTMLElement | null = document.getElementById(this._$elementId); - if (!element) { - - const player: Player = $currentPlayer(); - - const parent: HTMLElement | null = document - .getElementById(player.contentElementId); + const minSize: number = Math.ceil(Math.min(config.stage.width, config.stage.height) / 100); + const halfSize: number = minSize / 2; + for (let idx: number = 0; idx < 3; ++idx) { + + const sprite: Sprite = $sprite.getChildAt(idx); + + /** + * 初期値を設定 + * Set initial values + */ + sprite.scaleX = 0.1; + sprite.scaleY = 0.1; + sprite.alpha = 0; + + const reduceJob: Job = sprite.getLocalVariable("reduceJob"); + // reset + reduceJob.from = getEndObject(); + reduceJob.to = getStartObject(); + + const expandJob: Job = sprite.getLocalVariable("expandJob"); + // reset + expandJob.from = getStartObject(); + expandJob.to = getEndObject(); + + if (idx) { + setTimeout((): void => + { + expandJob.start(); + }, 200 * idx); + } else { + expandJob.start(); + } - if (!parent) { - return ; + const shape: Shape = sprite.getChildAt(0); + if (shape.width === minSize) { + continue; } - const loader: HTMLDivElement = document.createElement("div"); - - loader.id = this._$elementId; - - loader.innerHTML = `
-`; - - parent.insertBefore(loader, parent.children[0]); - - } else { - - element.setAttribute("style", ""); + shape + .graphics + .clear() + .beginFill("#ffffff") + .drawCircle(0, 0, halfSize); + sprite.x = minSize * 2 * idx; } + $sprite.x = (config.stage.width - $sprite.width) / 2; + $sprite.y = (config.stage.height - $sprite.height) / 2; + context.root.addChild($sprite); } /** @@ -121,11 +173,19 @@ export class DefaultLoading */ end (): void { - const element: HTMLElement | null = document - .getElementById(this._$elementId); + // stop job + for (let idx: number = 0; idx < 3; ++idx) { + const sprite: Sprite = $sprite.getChildAt(idx); + + const expandJob: Job = sprite.getLocalVariable("expandJob"); + expandJob.stop(); + + const reduceJob: Job = sprite.getLocalVariable("reduceJob"); + reduceJob.stop(); + } - if (element) { - element.setAttribute("style", "display:none;"); + if ($sprite.parent === context.root) { + context.root.removeChild($sprite); } } -} +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ebbf0d5..4a1e71b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,40 +1,39 @@ import "@next2d/player"; import { Application } from "./application/Application"; -import { DefaultLoading } from "./domain/screen/DefaultLoading"; import { View } from "./view/View"; import { ViewModel } from "./view/ViewModel"; import { MovieClipContent } from "./application/content/MovieClipContent"; import { ShapeContent } from "./application/content/ShapeContent"; import { TextFieldContent } from "./application/content/TextFieldContent"; import { VideoContent } from "./application/content/VideoContent"; -import { ConfigImpl } from "./interface/ConfigImpl"; import { packages } from "./application/variable/Packages"; import { context } from "./application/variable/Context"; import { cache } from "./application/variable/Cache"; import { query } from "./application/variable/Query"; import { response } from "./application/variable/Response"; import { loaderInfoMap } from "./application/variable/LoaderInfoMap"; +import type { ConfigImpl } from "./interface/ConfigImpl"; // output build version -console.log("%c Next2D Framework %c 1.6.1 %c https://next2d.app", +console.log("%c Next2D Framework %c 2.0.0 %c https://next2d.app", "color: #fff; background: #5f5f5f", "color: #fff; background: #4bc729", ""); +const app: Application = new Application(); export { - Application, - DefaultLoading, + app, View, ViewModel, MovieClipContent, ShapeContent, TextFieldContent, VideoContent, - ConfigImpl, packages, context, cache, query, response, - loaderInfoMap + loaderInfoMap, + ConfigImpl }; diff --git a/src/infrastructure/constant/RequestType.ts b/src/infrastructure/constant/RequestType.ts deleted file mode 100644 index 8bbf0a1..0000000 --- a/src/infrastructure/constant/RequestType.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { RequestTypeImpl } from "../../interface/RequestTypeImpl"; - -/** - * リクエストタイプの定数 - * Request Type Constants - * - * @class - * @memberof infrastructure.constant - */ -export class RequestType -{ - /** - * @description リクエストの集合体 - * Aggregation of requests. - * - * @return {string} - * @default "cluster" - * @const - * @static - */ - static get CLUSTER (): RequestTypeImpl - { - return "cluster"; - } - - /** - * @description NoCode Toolで書き出したJSONコンテンツ - * JSON content exported by NoCode Tool. - * - * @return {string} - * @default "content" - * @const - * @static - */ - static get CONTENT (): RequestTypeImpl - { - return "content"; - } - - /** - * @description カスタムリクエストタイプ - * custom request type. - * - * @return {string} - * @default "custom" - * @const - * @static - */ - static get CUSTOM (): RequestTypeImpl - { - return "custom"; - } - - /** - * @description JSONのフォーマットを指定 - * Specify JSON format. - * - * @return {string} - * @default "json" - * @const - * @static - */ - static get JSON (): RequestTypeImpl - { - return "json"; - } -} diff --git a/src/infrastructure/dto/ResponseDTO.test.ts b/src/infrastructure/dto/ResponseDTO.test.ts new file mode 100644 index 0000000..a77fde5 --- /dev/null +++ b/src/infrastructure/dto/ResponseDTO.test.ts @@ -0,0 +1,18 @@ +import { ResponseDTO } from "./ResponseDTO"; + +describe("ResponseDTOTest", () => +{ + test("execute test case1", () => + { + const responseDTO = new ResponseDTO(); + expect(responseDTO.name).toBe(""); + expect(responseDTO.response).toBe(null); + }); + + test("execute test case2", () => + { + const responseDTO = new ResponseDTO("sample", 100); + expect(responseDTO.name).toBe("sample"); + expect(responseDTO.response).toBe(100); + }); +}); \ No newline at end of file diff --git a/src/infrastructure/dto/ResponseDTO.ts b/src/infrastructure/dto/ResponseDTO.ts index f60f617..e968256 100644 --- a/src/infrastructure/dto/ResponseDTO.ts +++ b/src/infrastructure/dto/ResponseDTO.ts @@ -1,6 +1,6 @@ /** - * 外部データをObjectに変換(DTO)、可変性はない使い捨てのクラス - * Converts external data to Object (DTO), disposable class with no variability + * @description 外部データをObjectに変換(DTO)、可変性のない使い捨てのクラス + * Converts external data to Objects (DTO), non-variable, disposable class * * @class * @memberof infrastructure.dto @@ -34,10 +34,11 @@ export class ResponseDTO } /** - * @description キャッシュする場合のキー名 - * Key name if caching + * @description キャッシュのキー名 + * Key name of cache * * @return {string} + * @default "" * @readonly * @public */ @@ -51,10 +52,11 @@ export class ResponseDTO * response data * * @return {*} + * @default null * @readonly * @public */ - get response (): any + get response (): any | null { return this._$response; } diff --git a/src/infrastructure/repository/ContentRepository.ts b/src/infrastructure/repository/ContentRepository.ts index 3297d21..e4e69fe 100644 --- a/src/infrastructure/repository/ContentRepository.ts +++ b/src/infrastructure/repository/ContentRepository.ts @@ -1,4 +1,5 @@ -import { parser } from "../../application/variable/Parser"; +import type { RequestImpl } from "src/interface/RequestImpl"; +import { Loader } from "@next2d/display"; import { Event, IOErrorEvent @@ -7,89 +8,71 @@ import { URLRequestHeader, URLRequest } from "@next2d/net"; -import { Loader } from "@next2d/display"; - -interface Object { - type: string; - name: string; - path: string; - cache?: boolean; - callback?: string|string[]; - method?: string; - body?: object; - headers?: HeadersInit; -} /** - * NoCodeToolで制作したJSON取得時のリクエストとレスポンスの管理クラス - * Request and Response management class for JSON acquisition + * @description 指定先のJSONを非同期で取得 + * Asynchronously obtain JSON of the specified destination * - * @class - * @memberof infrastructure.repository + * @param {object} request_object + * @return {Promise} + * @method + * @public */ -export class ContentRepository +export const execute = (request_object: RequestImpl): Promise => { - /** - * @description 指定先のJSONを非同期で取得 - * Asynchronously obtain JSON of the specified destination - * - * @param {object} object - * @return {Promise} - * @method - * @public - */ - execute (object: Object): Promise + return new Promise((resolve, reject) => { - return new Promise((resolve, reject) => - { - const request: URLRequest = new URLRequest(`${parser.execute(object.path)}`); + if (!request_object.path) { + return reject(); + } - const method = object.method - ? parser.execute(object.method).toUpperCase() - : "GET"; + const request: URLRequest = new URLRequest(request_object.path); - switch (method) { + const method: string = request_object.method + ? request_object.method.toUpperCase() + : "GET"; - case "DELETE": - case "GET": - case "HEAD": - case "OPTIONS": - case "POST": - case "PUT": - request.method = method; - break; + switch (method) { - default: - request.method = "GET"; - break; + case "DELETE": + case "GET": + case "HEAD": + case "OPTIONS": + case "POST": + case "PUT": + request.method = method; + break; - } + default: + request.method = "GET"; + break; - if (object.headers) { - for (const [name, value] of Object.entries(object.headers)) { - request - .requestHeaders - .push(new URLRequestHeader(name, value)); - } - } + } - if (object.body) { - request.data = JSON.stringify(object.body); + if (request_object.headers) { + for (const [name, value] of Object.entries(request_object.headers)) { + request + .requestHeaders + .push(new URLRequestHeader(name, value)); } + } + + if (request_object.body) { + request.data = JSON.stringify(request_object.body); + } - const loader: Loader = new Loader(); - loader - .contentLoaderInfo - .addEventListener(Event.COMPLETE, (event: any) => - { - return resolve(event.currentTarget.content); - }); + const loader: Loader = new Loader(); + loader + .contentLoaderInfo + .addEventListener(Event.COMPLETE, (event: Event) => + { + return resolve(event.currentTarget.content); + }); - loader - .contentLoaderInfo - .addEventListener(IOErrorEvent.IO_ERROR, reject); + loader + .contentLoaderInfo + .addEventListener(IOErrorEvent.IO_ERROR, reject); - loader.load(request); - }); - } -} \ No newline at end of file + loader.load(request); + }); +}; \ No newline at end of file diff --git a/src/infrastructure/repository/CustomRepository.ts b/src/infrastructure/repository/CustomRepository.ts index 612faa3..88fbc1f 100644 --- a/src/infrastructure/repository/CustomRepository.ts +++ b/src/infrastructure/repository/CustomRepository.ts @@ -1,56 +1,40 @@ -import { parser } from "../../application/variable/Parser"; +import type { RequestImpl } from "src/interface/RequestImpl"; import { packages } from "../../application/variable/Packages"; -interface Object { - type: string; - name: string; - path: string; - cache?: boolean; - class: string; - access: string; - method: string; - callback?: string|string[]; - body?: object; - headers?: HeadersInit; -} - /** - * 外部データ取得時のリクエストとレスポンスの管理クラス - * Request and Response management class for JSON acquisition + * @description 指定先の外部データを非同期で取得 + * Asynchronous acquisition of external data at specified destination * - * @class - * @memberof infrastructure.repository + * @param {object} request_object + * @return {Promise} + * @method + * @public */ -export class CustomRepository +export const execute = (request_object: RequestImpl): Promise => { - /** - * @description 指定先の外部データを非同期で取得 - * Asynchronous acquisition of external data at specified destination - * - * @param {object} object - * @return {Promise} - * @method - * @public - */ - execute (object: Object): Promise + return new Promise((resolve) => { - return new Promise((resolve) => - { - const className: string = parser.execute(object.class); - if (!packages.has(className)) { - return resolve(null); - } + if (!request_object.class + || !request_object.access + || !request_object.method + ) { + return resolve(null); + } + + const name: string = request_object.class; + if (!name || !packages.has(name)) { + return resolve(null); + } - const CallbackClass: any = packages.get(className); - const promise: Promise = parser.execute(object.access) === "static" - ? Promise.resolve(CallbackClass[parser.execute(object.method)]()) - : Promise.resolve(new CallbackClass()[parser.execute(object.method)]()); + const CallbackClass: any = packages.get(name); + const promise: Promise = request_object.access === "static" + ? Promise.resolve(CallbackClass[request_object.method]()) + : Promise.resolve(new CallbackClass()[request_object.method]()); - return promise - .then((value: any) => - { - return resolve(value); - }); - }); - } -} \ No newline at end of file + return promise + .then((value: any) => + { + return resolve(value); + }); + }); +}; \ No newline at end of file diff --git a/src/infrastructure/repository/JsonRepository.ts b/src/infrastructure/repository/JsonRepository.ts index 77cf488..f6db23d 100644 --- a/src/infrastructure/repository/JsonRepository.ts +++ b/src/infrastructure/repository/JsonRepository.ts @@ -1,59 +1,39 @@ -import { parser } from "../../application/variable/Parser"; - -interface Object { - type: string; - name: string; - path: string; - cache?: boolean; - callback?: string | string[]; - method?: string; - body?: object; - headers?: HeadersInit; -} +import type { RequestImpl } from "src/interface/RequestImpl"; /** - * JSON取得時のリクエストとレスポンスの管理クラス - * Request and Response management class for JSON acquisition + * @description 指定先のJSONを非同期で取得 + * Asynchronously obtain JSON of the specified destination * - * @class - * @memberof infrastructure.repository + * @param {object} request_object + * @return {Promise} + * @method + * @public */ -export class JsonRepository +export const execute = async (request_object: RequestImpl): Promise => { - /** - * @description 指定先のJSONを非同期で取得 - * Asynchronously obtain JSON of the specified destination - * - * @param {object} object - * @return {Promise} - * @method - * @public - */ - execute (object: Object): Promise - { - const options: RequestInit = {}; + if (!request_object.path) { + throw new Error("`path` must be set for json requests."); + } - const method: string = options.method = object.method - ? parser.execute(object.method).toUpperCase() - : "GET"; + const options: RequestInit = {}; - const body: any = object.body - && method === "POST" || method === "PUT" - ? JSON.stringify(object.body) - : null; + const method: string = options.method = request_object.method + ? request_object.method.toUpperCase() + : "GET"; - if (body) { - options.body = body; - } + const body: any = request_object.body + && method === "POST" || method === "PUT" + ? JSON.stringify(request_object.body) + : null; - if (object.headers) { - options.headers = object.headers; - } + if (body) { + options.body = body; + } - return fetch(`${parser.execute(object.path)}`, options) - .then((response: Response) => - { - return response.json(); - }); + if (request_object.headers) { + options.headers = request_object.headers; } -} \ No newline at end of file + + const response: Response = await fetch(request_object.path, options); + return await response.json(); +}; \ No newline at end of file diff --git a/src/infrastructure/service/ContentService.ts b/src/infrastructure/service/ContentService.ts index 1a965d0..2ad895d 100644 --- a/src/infrastructure/service/ContentService.ts +++ b/src/infrastructure/service/ContentService.ts @@ -1,128 +1,95 @@ -import { ContentRepository } from "../repository/ContentRepository"; -import { Callback } from "../../domain/callback/Callback"; +import { execute as contentRepository } from "../repository/ContentRepository"; +import { execute as callback } from "../../domain/callback/Callback"; import { ResponseDTO } from "../dto/ResponseDTO"; -import { parser } from "../../application/variable/Parser"; import { cache } from "../../application/variable/Cache"; import { loaderInfoMap } from "../../application/variable/LoaderInfoMap"; import type { LoaderInfo } from "@next2d/display"; - -interface Object { - type: string; - name: string; - path: string; - cache?: boolean; - callback?: string | string[]; - method?: string; - body?: object; - headers?: HeadersInit; -} +import type { RequestImpl } from "src/interface/RequestImpl"; /** - * NoCodeToolで制作したJSON取得時のロジッククラス - * Logic class for JSON acquisition produced by NoCodeTool + * @description RepositoryからJSONを取得して、configのcallbackがあれば実行 + * キャッシュ設定がOnの時はJSONをキャッシュにセット + * Get JSON from Repository and run config callback if any. + * If cache setting is On, set JSON to cache. * - * @class - * @memberof infrastructure.service + * @param {object} request_object + * @return {Promise} + * @method + * @public */ -export class ContentService +export const execute = async (request_object: RequestImpl): Promise => { - private _$repository: ContentRepository; - private _$callback: Callback; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {ContentRepository} - * @private - */ - this._$repository = new ContentRepository(); - - /** - * @type {Callback} - * @private - */ - this._$callback = new Callback(); + if (!request_object.name) { + throw new Error("`name` must be set for content requests."); } /** - * @description RepositoryからJSONを取得して、configのcallbackがあれば実行 - * キャッシュ設定がOnの時はJSONをキャッシュにセット - * Get JSON from Repository and run config callback if any. - * If cache setting is On, set JSON to cache. - * - * @param {object} object - * @return {Promise} - * @method - * @public + * キャッシュを利用する場合はキャッシュデータをチェック + * Check cache data if cache is used */ - execute (object: Object): Promise - { - /** - * キャッシュを利用する場合はキャッシュデータをチェック - * Check cache data if cache is used - */ - if (object.cache && object.name) { + if (request_object.cache) { - const name: string = parser.execute(object.name); - if (cache.size && cache.has(name)) { + if (cache.size && cache.has(request_object.name)) { - const value: any = cache.get(name); + const value: any = cache.get(request_object.name); + /** + * コールバック設定があれば実行 + * Execute callback settings if any. + */ + if (request_object.callback) { const promises: Promise[]|void>[] = []; - if (object.callback) { - promises.push(this._$callback.execute( - object.callback, value - )); - } + promises.push(callback( + request_object.callback, value + )); - return Promise - .all(promises) - .then((): ResponseDTO => - { - return new ResponseDTO(name, value); - }); + await Promise.all(promises); } + + return new ResponseDTO(request_object.name, value); } + } - return this - ._$repository - .execute(object) - .then((content: any) => - { - const name: string = parser.execute(object.name); - if (object.cache && object.name) { - cache.set(name, content); - } + /** + * 指定のコンテンツデータを取得 + * Obtain specified content data + */ + const content = await contentRepository(request_object); - const loaderInfo: LoaderInfo = content._$loaderInfo; + /** + * キャッシュ設定がonならキャッシュに登録 + * If the cache setting is on, register it in the cache. + */ + if (request_object.cache) { + cache.set(request_object.name, content); + } - // DisplayObjectContainer - if (loaderInfo._$data) { - const symbols: Map = loaderInfo._$data.symbols; - if (symbols.size) { - for (const name of symbols.keys()) { - loaderInfoMap.set(name, loaderInfo); - } - } - } + /** + * Animation Toolで設定したシンボルをマップに登録 + * Register the symbols set by Animation Tool to the map + */ + const loaderInfo: LoaderInfo = content._$loaderInfo as NonNullable; + if (loaderInfo._$data) { + const symbols: Map = loaderInfo._$data.symbols; + if (symbols.size) { + for (const name of symbols.keys()) { + loaderInfoMap.set(name, loaderInfo); + } + } + } - const promises: Promise[]|void>[] = []; - if (object.callback) { - promises.push(this._$callback.execute( - object.callback, content - )); - } + /** + * コールバック設定があれば実行 + * Execute callback settings if any. + */ + if (request_object.callback) { + const promises: Promise[]|void>[] = []; + promises.push(callback( + request_object.callback, content + )); - return Promise - .all(promises) - .then((): ResponseDTO => - { - return new ResponseDTO(name, content); - }); - }); + await Promise.all(promises); } -} \ No newline at end of file + + return new ResponseDTO(request_object.name, content); +}; \ No newline at end of file diff --git a/src/infrastructure/service/CustomService.ts b/src/infrastructure/service/CustomService.ts index 8253869..26eefcf 100644 --- a/src/infrastructure/service/CustomService.ts +++ b/src/infrastructure/service/CustomService.ts @@ -1,116 +1,63 @@ -import { CustomRepository } from "../repository/CustomRepository"; -import { Callback } from "../../domain/callback/Callback"; +import type { RequestImpl } from "src/interface/RequestImpl"; import { ResponseDTO } from "../dto/ResponseDTO"; -import { parser } from "../../application/variable/Parser"; import { cache } from "../../application/variable/Cache"; - -interface Object { - type: string; - name: string; - path: string; - cache?: boolean; - class: string; - access: string; - method: string; - callback?: string | string[]; - body?: object; - headers?: HeadersInit; -} +import { execute as callback } from "../../domain/callback/Callback"; +import { execute as customRepository } from "../repository/CustomRepository"; /** - * JSON取得時のロジッククラス - * Logic class for JSON acquisition + * @description Repositoryから外部データを取得して、configのcallbackがあれば実行 + * キャッシュ設定がOnの時はJSONをキャッシュにセット + * Retrieve external data from Repository and run config callback if any. + * If cache setting is On, set JSON to cache. * - * @class - * @memberof infrastructure.service + * @param {object} request_object + * @return {Promise} + * @method + * @public */ -export class CustomService +export const execute = async (request_object: RequestImpl): Promise => { - private readonly _$repository: CustomRepository; - private readonly _$callback: Callback; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {CustomRepository} - * @private - */ - this._$repository = new CustomRepository(); - - /** - * @type {Callback} - * @private - */ - this._$callback = new Callback(); + if (!request_object.name) { + throw new Error("`name` must be set for custom requests."); } /** - * @description Repositoryから外部データを取得して、configのcallbackがあれば実行 - * キャッシュ設定がOnの時はJSONをキャッシュにセット - * Retrieve external data from Repository and run config callback if any. - * If cache setting is On, set JSON to cache. - * - * @param {object} object - * @return {Promise} - * @method - * @public + * キャッシュを利用する場合はキャッシュデータをチェック + * Check cache data if cache is used */ - execute (object: Object): Promise - { - /** - * キャッシュを利用する場合はキャッシュデータをチェック - * Check cache data if cache is used - */ - if (object.cache && object.name) { + if (request_object.cache) { - const name: string = parser.execute(object.name); - if (cache.size && cache.has(name)) { + if (cache.size && cache.has(request_object.name)) { - const value: any = cache.get(name); + const value: any = cache.get(request_object.name); + if (request_object.callback) { const promises: Promise[]|void>[] = []; - if (object.callback) { - promises.push(this._$callback.execute( - object.callback, value - )); - } + promises.push(callback( + request_object.callback, value + )); - return Promise - .all(promises) - .then((): ResponseDTO => - { - return new ResponseDTO(name, value); - }); + await Promise.all(promises); } + + return new ResponseDTO(request_object.name, value); } + } - return this - ._$repository - .execute(object) - .then((response: any) => - { - const name:string = parser.execute(object.name); - if (object.cache && object.name) { - cache.set(name, response); - } + const response: any = await customRepository(request_object); - const promises: Promise[]|void>[] = []; - if (object.callback) { - promises.push(this._$callback.execute( - object.callback, response - )); - } + if (request_object.cache) { + cache.set(request_object.name, response); + } - return Promise - .all(promises) - .then((): ResponseDTO => - { - return new ResponseDTO(name, response); - }); - }); + if (request_object.callback) { + const promises: Promise[]|void>[] = []; + promises.push(callback( + request_object.callback, response + )); + + await Promise.all(promises); } -} \ No newline at end of file + + return new ResponseDTO(request_object.name, response); +}; \ No newline at end of file diff --git a/src/infrastructure/service/JsonService.ts b/src/infrastructure/service/JsonService.ts index a60fa97..32f13e5 100644 --- a/src/infrastructure/service/JsonService.ts +++ b/src/infrastructure/service/JsonService.ts @@ -1,114 +1,62 @@ -import { JsonRepository } from "../repository/JsonRepository"; -import { Callback } from "../../domain/callback/Callback"; +import { execute as jsonRepository } from "../repository/JsonRepository"; +import { execute as callback } from "../../domain/callback/Callback"; import { ResponseDTO } from "../dto/ResponseDTO"; -import { parser } from "../../application/variable/Parser"; import { cache } from "../../application/variable/Cache"; - -interface Object { - type: string; - name: string; - path: string; - cache?: boolean; - callback?: string | string[]; - method?: string; - body?: object; - headers?: HeadersInit; -} +import { RequestImpl } from "src/interface/RequestImpl"; /** - * JSON取得時のロジッククラス - * Logic class for JSON acquisition + * @description RepositoryからJSONを取得して、configのcallbackがあれば実行 + * キャッシュ設定がOnの時はJSONをキャッシュにセット + * Get JSON from Repository and run config callback if any. + * If cache setting is On, set JSON to cache. * - * @class - * @memberof infrastructure.service + * @param {object} request_object + * @return {Promise} + * @method + * @public */ -export class JsonService +export const execute = async (request_object: RequestImpl): Promise => { - private readonly _$repository: JsonRepository; - private readonly _$callback: Callback; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {JsonRepository} - * @private - */ - this._$repository = new JsonRepository(); - - /** - * @type {Callback} - * @private - */ - this._$callback = new Callback(); + if (!request_object.name) { + throw new Error("`name` must be set for json requests."); } /** - * @description RepositoryからJSONを取得して、configのcallbackがあれば実行 - * キャッシュ設定がOnの時はJSONをキャッシュにセット - * Get JSON from Repository and run config callback if any. - * If cache setting is On, set JSON to cache. - * - * @param {object} object - * @return {Promise} - * @method - * @public + * キャッシュを利用する場合はキャッシュデータをチェック + * Check cache data if cache is used */ - execute (object: Object): Promise - { - /** - * キャッシュを利用する場合はキャッシュデータをチェック - * Check cache data if cache is used - */ - if (object.cache && object.name) { - - const name: string = parser.execute(object.name); - if (cache.size && cache.has(name)) { + if (request_object.cache) { + if (cache.size && cache.has(request_object.name)) { - const value: any = cache.get(name); + const value: any = cache.get(request_object.name); + if (request_object.callback) { const promises: Promise[]|void>[] = []; - if (object.callback) { - promises.push(this._$callback.execute( - object.callback, value - )); - } + promises.push(callback( + request_object.callback, value + )); - return Promise - .all(promises) - .then((): ResponseDTO => - { - return new ResponseDTO(name, value); - }); + await Promise.all(promises); } + + return new ResponseDTO(request_object.name, value); } + } - return this - ._$repository - .execute(object) - .then((response: JSON) => - { - const name: string = parser.execute(object.name); - if (object.cache && object.name) { - cache.set(name, response); - } + const response: any = await jsonRepository(request_object); - const promises: Promise[]|void>[] = []; - if (object.callback) { - promises.push(this._$callback.execute( - object.callback, response - )); - } + if (request_object.cache) { + cache.set(request_object.name, response); + } + + if (request_object.callback) { + const promises: Promise[]|void>[] = []; + promises.push(callback( + request_object.callback, response + )); - return Promise - .all(promises) - .then((): ResponseDTO => - { - return new ResponseDTO(name, response); - }); - }); + await Promise.all(promises); } -} \ No newline at end of file + + return new ResponseDTO(request_object.name, response); +}; \ No newline at end of file diff --git a/src/infrastructure/usecase/RequestUseCase.ts b/src/infrastructure/usecase/RequestUseCase.ts index 77fa84c..f411534 100644 --- a/src/infrastructure/usecase/RequestUseCase.ts +++ b/src/infrastructure/usecase/RequestUseCase.ts @@ -1,112 +1,44 @@ -import { RequestType } from "../constant/RequestType"; -import { ContentService } from "../service/ContentService"; -import { CustomService } from "../service/CustomService"; -import { JsonService } from "../service/JsonService"; -import { Callback } from "../../domain/callback/Callback"; -import { RequestParser } from "../../domain/parser/RequestParser"; -import { parser } from "../../application/variable/Parser"; +import { execute as contentService } from "../service/ContentService"; +import { execute as customService } from "../service/CustomService"; +import { execute as jsonService } from "../service/JsonService"; +import { execute as requestParser } from "../../domain/parser/RequestParser"; import type { ResponseDTO } from "../dto/ResponseDTO"; - -interface Object { - type: string; - name: string; - path: string; - cache?: boolean; - class: string; - access: string; - method: string; - callback?: string | string[]; -} +import type { RequestImpl } from "src/interface/RequestImpl"; /** - * ページの切り替え時の外部リクエストクラス - * External request class when switching pages + * @description Routing設定で指定したタイプへリクエストを実行 + * Execute requests to the type specified in Routing settings * - * @class - * @memberof infrastructure.usecase + * @param {string} name + * @return {Promise} + * @method + * @public */ -export class RequestUseCase +export const execute = (name: string): Promise[] => { - private readonly _$callback: Callback; - private readonly _$contentService: ContentService; - private readonly _$customService: CustomService; - private readonly _$jsonService: JsonService; - private readonly _$requestParser: RequestParser; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {Callback} - * @private - */ - this._$callback = new Callback(); - - /** - * @type {ContentService} - * @private - */ - this._$contentService = new ContentService(); - - /** - * @type {CustomService} - * @private - */ - this._$customService = new CustomService(); - - /** - * @type {JsonService} - * @private - */ - this._$jsonService = new JsonService(); + const promises: Promise[] = []; + const requests: RequestImpl[] = requestParser(name); + for (let idx: number = 0; idx < requests.length; ++idx) { - /** - * @type {RequestParser} - * @private - */ - this._$requestParser = new RequestParser(); - } - - /** - * @description Routing設定で指定したタイプへリクエストを実行 - * Execute requests to the type specified in Routing settings - * - * @param {string} name - * @return {Promise} - * @method - * @public - */ - execute (name: string): Promise[] - { - const promises: Promise[] = []; - const requests: Object[] = this._$requestParser.execute(name); - for (let idx: number = 0; idx < requests.length; ++idx) { - - const object: Object = requests[idx]; - switch (parser.execute(object.type)) { + const requestObject: RequestImpl = requests[idx]; + switch (requestObject.type) { - case RequestType.CUSTOM: - promises.push( - this._$customService.execute(object) - ); - break; + case "custom": + promises.push(customService(requestObject)); + break; - case RequestType.JSON: - promises.push( - this._$jsonService.execute(object) - ); - break; + case "json": + promises.push(jsonService(requestObject)); + break; - case RequestType.CONTENT: - promises.push(this._$contentService.execute(object)); - break; + case "content": + promises.push(contentService(requestObject)); + break; - } + default: + break; } - - return promises; } -} \ No newline at end of file + + return promises; +}; \ No newline at end of file diff --git a/src/interface/ConfigImpl.ts b/src/interface/ConfigImpl.ts index e877e9b..ebbf461 100644 --- a/src/interface/ConfigImpl.ts +++ b/src/interface/ConfigImpl.ts @@ -1,6 +1,5 @@ import { StageImpl } from "./StageImpl"; import { RoutingImpl } from "./RoutingImpl"; -import { LoadingImpl } from "./LoadingImpl"; import { GotoViewImpl } from "./GotoViewImpl"; interface BaseConfigImpl { @@ -14,6 +13,8 @@ export interface ConfigImpl extends BaseConfigImpl { [key: string]: RoutingImpl }; spa: boolean; - loading?: LoadingImpl; + loading?: { + callback: string; + }; gotoView?: GotoViewImpl; } \ No newline at end of file diff --git a/src/interface/LoadingImpl.ts b/src/interface/LoadingImpl.ts index 64e0859..12a8926 100644 --- a/src/interface/LoadingImpl.ts +++ b/src/interface/LoadingImpl.ts @@ -1,3 +1,4 @@ export interface LoadingImpl { - callback: string; + start: Function; + end: Function; } \ No newline at end of file diff --git a/src/interface/QueryObjectImpl.ts b/src/interface/QueryObjectImpl.ts new file mode 100644 index 0000000..00d3021 --- /dev/null +++ b/src/interface/QueryObjectImpl.ts @@ -0,0 +1,4 @@ +export interface QueryObjectImpl { + name: string; + queryString: string; +} \ No newline at end of file diff --git a/src/interface/RequestImpl.ts b/src/interface/RequestImpl.ts index 347f7d1..9b5c80b 100644 --- a/src/interface/RequestImpl.ts +++ b/src/interface/RequestImpl.ts @@ -9,4 +9,6 @@ export interface RequestImpl { class?: string; access?: string; method?: string; + headers?: HeadersInit; + body?: object; } \ No newline at end of file diff --git a/src/interface/RoutingImpl.ts b/src/interface/RoutingImpl.ts index 4884e9e..4e6b886 100644 --- a/src/interface/RoutingImpl.ts +++ b/src/interface/RoutingImpl.ts @@ -3,4 +3,5 @@ import { RequestImpl } from "./RequestImpl"; export interface RoutingImpl { private?: boolean; requests?: RequestImpl[]; + redirect?: string; } \ No newline at end of file diff --git a/src/view/ViewModel.ts b/src/view/ViewModel.ts index 1526210..78996a6 100644 --- a/src/view/ViewModel.ts +++ b/src/view/ViewModel.ts @@ -32,6 +32,7 @@ export class ViewModel * @method * @public */ + // @ts-ignore // eslint-disable-next-line no-unused-vars,no-empty-function unbind (view: View): void {} @@ -48,7 +49,8 @@ export class ViewModel { return new Promise((resolve) => { - requestAnimationFrame((): void => { + requestAnimationFrame((): void => + { return resolve(view); }); }); diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 7383046..16a9068 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -6,6 +6,7 @@ ], "exclude": [ "node_modules", + "src/**/*.test.ts", "dist" ] } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 1cb4ad3..25304a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,21 +1,36 @@ { "compilerOptions": { - "strict": true, - "resolveJsonModule": true, - "esModuleInterop": true, + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "declaration": true, - "allowJs": true, - "target": "es6", - "module": "es6", - "moduleResolution": "node", "baseUrl": ".", - "outDir": "./dist/" + "outDir": "./dist", + + "types": [ + "vitest/globals" + ] }, "include": [ "src/**/*.ts", - "src/index.ts", "@types/**/*.ts" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules", + "src/**/*.test.ts" + ] } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..6d71f23 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,11 @@ +/// + +import { defineConfig } from "vite"; + +export default defineConfig({ + "test": { + "globals": true, + "environment": "jsdom", + "include": ["src/**/*.test.ts"] + } +}); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 23e1d8f..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,40 +0,0 @@ -const path = require("path"); -const ESLintPlugin = require("eslint-webpack-plugin"); - -module.exports = { - "mode": "production", - "entry": path.resolve(__dirname, "src/index.ts"), - "output": { - "filename": "next2d-framework.js", - "path": path.resolve(__dirname, "dist") - }, - "plugins": [ - new ESLintPlugin({ - "extensions": [".ts", ".js"], - "exclude": "node_modules", - "fix": true - }) - ], - "module": { - "rules": [ - { - "test": /\.ts$/, - "use": "ts-loader", - "exclude": /node_modules/ - } - ] - }, - "resolve": { - "alias": { - "@": path.resolve(__dirname, "src") - }, - "extensions": [".ts", ".js"] - }, - "devServer": { - "static": { - "directory": path.resolve(__dirname, "dist") - }, - "compress": false, - "open": true - } -};