diff --git a/.gitignore b/.gitignore index 6704566..bc08c8c 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,12 @@ dist # TernJS port file .tern-port + +.DS_Store + +temp/ + +/temp/ + +__tests__/diff +# __tests__/snapshot diff --git a/api-extractor.json b/api-extractor.json new file mode 100644 index 0000000..ab9a6d3 --- /dev/null +++ b/api-extractor.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "apiReport": { + "enabled": true, + "reportFolder": "/temp/" + }, + + "docModel": { + "enabled": true + }, + + "dtsRollup": { + "enabled": true + }, + + "tsdocMetadata": { + "enabled": false + }, + + "messages": { + "compilerMessageReporting": { + "default": { + "logLevel": "warning" + } + }, + + "extractorMessageReporting": { + "default": { + "logLevel": "warning", + "addToApiReportFile": true + }, + + "ae-missing-release-tag": { + "logLevel": "none" + } + }, + + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + }, + + "tsdoc-undefined-tag": { + "logLevel": "none" + } + } + } +} diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..9ba90d5 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,6 @@ +module.exports = { + presets: [ + ['@babel/preset-env', {targets: {node: 'current'}}], + '@babel/preset-typescript', + ], +}; \ No newline at end of file diff --git a/dev.md b/dev.md new file mode 100644 index 0000000..2b07c26 --- /dev/null +++ b/dev.md @@ -0,0 +1,4 @@ + +```sh +lerna add @idraw/drag-types --scope=@idraw/drag --dev +``` \ No newline at end of file diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000..3224f4e --- /dev/null +++ b/lerna.json @@ -0,0 +1,8 @@ +{ + "packages": [ + "packages/types", + "packages/canvas", + "packages/idraw" + ], + "version": "0.0.1" +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f106af1 --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "root", + "private": false, + "scripts": { + "dev": "node ./scripts/dev.js", + "build": "node ./scripts/build.js", + "snapshot": "node ./scripts/build.js && node ./scripts/snapshot.js", + "e2e": "mocha --exit ./__tests__/e2e.test.js", + "init": "lerna bootstrap --npm-client=cnpm", + "clear": "rm -rf ./packages/*/dist/ & rm -rf ./packages/*/node_modules/", + "jest": "jest --config jest.config.js", + "test": "lerna bootstrap --no-ci && npm run build && npm run jest && npm run e2e" + }, + "devDependencies": { + "@babel/core": "^7.13.14", + "@babel/preset-env": "^7.13.12", + "@babel/preset-typescript": "^7.13.0", + "@microsoft/api-extractor": "^7.13.2", + "@rollup/plugin-node-resolve": "^11.2.1", + "babel-jest": "^26.6.3", + "chalk": "^4.1.0", + "execa": "^5.0.0", + "fs-extra": "^9.1.0", + "http-server": "^0.12.3", + "jest": "^26.6.3", + "jimp": "^0.16.1", + "koa-compose": "^4.1.0", + "lerna": "^3.22.1", + "less": "^4.1.1", + "mocha": "^8.3.2", + "pixelmatch": "^5.2.1", + "pngjs": "^6.0.0", + "postcss": "^8.2.9", + "puppeteer": "^8.0.0", + "rollup": "^2.40.0", + "rollup-plugin-postcss": "^4.0.0", + "rollup-plugin-typescript2": "^0.30.0", + "serve-handler": "^6.1.3", + "ts-node": "^9.1.1", + "tslib": "^2.2.0", + "typescript": "^4.2.3" + } +} diff --git a/packages/canvas/api-extractor.json b/packages/canvas/api-extractor.json new file mode 100644 index 0000000..7ae8eca --- /dev/null +++ b/packages/canvas/api-extractor.json @@ -0,0 +1,7 @@ +{ + "extends": "../../api-extractor.json", + "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", + "dtsRollup": { + "publicTrimmedFilePath": "./dist/index.d.ts" + } +} \ No newline at end of file diff --git a/packages/canvas/example/index.html b/packages/canvas/example/index.html new file mode 100644 index 0000000..d982bfb --- /dev/null +++ b/packages/canvas/example/index.html @@ -0,0 +1,18 @@ + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/packages/canvas/example/main.js b/packages/canvas/example/main.js new file mode 100644 index 0000000..c9cd42f --- /dev/null +++ b/packages/canvas/example/main.js @@ -0,0 +1 @@ +console.log('hello world') \ No newline at end of file diff --git a/packages/canvas/package.json b/packages/canvas/package.json new file mode 100644 index 0000000..68b612a --- /dev/null +++ b/packages/canvas/package.json @@ -0,0 +1,14 @@ +{ + "name": "@idraw/canvas", + "version": "0.0.1", + "description": "", + "main": "dist/index.cjs.js", + "module": "dist/index.es.js", + "unpkg": "dist/index.global.js", + "types": "dist/index.d.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "chenshenhai", + "license": "MIT" +} diff --git a/packages/canvas/src/index.ts b/packages/canvas/src/index.ts new file mode 100644 index 0000000..a408936 --- /dev/null +++ b/packages/canvas/src/index.ts @@ -0,0 +1,8 @@ + +class Canvas { + render() { + console.log('hello world') + } +} + +export default Canvas; \ No newline at end of file diff --git a/packages/idraw/api-extractor.json b/packages/idraw/api-extractor.json new file mode 100644 index 0000000..7ae8eca --- /dev/null +++ b/packages/idraw/api-extractor.json @@ -0,0 +1,7 @@ +{ + "extends": "../../api-extractor.json", + "mainEntryPointFilePath": "./dist/packages//src/index.d.ts", + "dtsRollup": { + "publicTrimmedFilePath": "./dist/index.d.ts" + } +} \ No newline at end of file diff --git a/packages/idraw/example/index.html b/packages/idraw/example/index.html new file mode 100644 index 0000000..d982bfb --- /dev/null +++ b/packages/idraw/example/index.html @@ -0,0 +1,18 @@ + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/packages/idraw/example/main.js b/packages/idraw/example/main.js new file mode 100644 index 0000000..c9cd42f --- /dev/null +++ b/packages/idraw/example/main.js @@ -0,0 +1 @@ +console.log('hello world') \ No newline at end of file diff --git a/packages/idraw/package.json b/packages/idraw/package.json new file mode 100644 index 0000000..68b612a --- /dev/null +++ b/packages/idraw/package.json @@ -0,0 +1,14 @@ +{ + "name": "@idraw/canvas", + "version": "0.0.1", + "description": "", + "main": "dist/index.cjs.js", + "module": "dist/index.es.js", + "unpkg": "dist/index.global.js", + "types": "dist/index.d.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "chenshenhai", + "license": "MIT" +} diff --git a/packages/idraw/src/index.ts b/packages/idraw/src/index.ts new file mode 100644 index 0000000..d7fc206 --- /dev/null +++ b/packages/idraw/src/index.ts @@ -0,0 +1,8 @@ + +class IDraw { + render() { + console.log('hello world') + } +} + +export default IDraw; \ No newline at end of file diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 0000000..05cf641 --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,17 @@ +{ + "name": "@idraw/idraw-types", + "version": "0.0.1", + "description": "", + "main": "src/index.ts", + "types": "src/index.ts", + "scripts": {}, + "author": "chenshenhai", + "license": "MIT", + "files": [ + "src", + "index.ts" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 0000000..22a79de --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1 @@ +export * from './lib/data'; \ No newline at end of file diff --git a/packages/types/src/lib/data.ts b/packages/types/src/lib/data.ts new file mode 100644 index 0000000..693da49 --- /dev/null +++ b/packages/types/src/lib/data.ts @@ -0,0 +1 @@ +export {} \ No newline at end of file diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..1a0a26d --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,25 @@ +const path = require('path'); +const fs = require('fs-extra'); +const execa = require('execa'); +const chalk = require('chalk'); +// const { Extractor, ExtractorConfig } = require('@microsoft/api-extractor'); +const { packages } = require('./config'); +const pkgNames = packages.map((pkg) => { + return pkg.dirName +}) + +async function main() { + + pkgNames.forEach(async (name) => { + const target = name; + const pkgDir = path.resolve(`packages/${target}`); + // const pkg = require(`${pkgDir}/package.json`) + await fs.remove(`${pkgDir}/dist`); + }); + + await + execa('rollup', [ '-c', './scripts/rollup.config.js', ], { stdio: 'inherit' }); + +} + +main(); \ No newline at end of file diff --git a/scripts/config.js b/scripts/config.js new file mode 100644 index 0000000..c677aa4 --- /dev/null +++ b/scripts/config.js @@ -0,0 +1,12 @@ +module.exports = { + packages: [ + { + dirName: 'canvas', + globalName: 'iDraw.Canvas', + }, + { + dirName: 'idraw', + globalName: 'iDraw.IDraw', + }, + ] +} \ No newline at end of file diff --git a/scripts/dev.js b/scripts/dev.js new file mode 100644 index 0000000..2401f17 --- /dev/null +++ b/scripts/dev.js @@ -0,0 +1,14 @@ +const execa = require('execa'); + +async function main() { + + await execa( 'rollup', + [ + '-w', + '-c', + './scripts/rollup.config.js' + ], { stdio: 'inherit' } + ) +} + +main(); \ No newline at end of file diff --git a/scripts/rollup.config.js b/scripts/rollup.config.js new file mode 100644 index 0000000..0d02e80 --- /dev/null +++ b/scripts/rollup.config.js @@ -0,0 +1,78 @@ +const path = require('path'); +const typescript = require('rollup-plugin-typescript2'); +const { nodeResolve } = require('@rollup/plugin-node-resolve'); +const { packages } = require('./config'); +const dtsPlugin = require('./util/dts-plugin'); +const stylePlugin = require('./util/style-plugin'); + +const resolveFile = function(names = []) { + return path.join(__dirname, '..', 'packages', ...names) +} + +const modules = []; +const external = [ '@idraw/idraw-types']; + +for(let i = 0; i < packages.length; i++) { + const pkg = packages[i]; + modules.push({ + input: resolveFile([pkg.dirName, 'src', 'index.ts']), + output: resolveFile([pkg.dirName, 'dist', 'index.global.js']), + name: pkg.globalName, + format: 'iife', + plugins: [] + }); + modules.push({ + input: resolveFile([pkg.dirName, 'src', 'index.ts']), + output: resolveFile([pkg.dirName, 'dist', 'index.cjs.js']), + name: pkg.globalName, + format: 'cjs', + exports: 'default', + plugins: [dtsPlugin(pkg.dirName),], + external, + }); + modules.push({ + input: resolveFile([pkg.dirName, 'src', 'index.ts']), + output: resolveFile([pkg.dirName, 'dist', 'index.es.js']), + name: pkg.globalName, + esModule: true, + format: 'es', + external, + plugins: [dtsPlugin(pkg.dirName),] + }); +} + + +function createConfigItem(params) { + const { input, output, name, format, plugins = [], esModule, exports} = params; + return { + input: input, + output: { + file:output, + format, + name: name, + esModule: esModule === true, + sourcemap: true, + exports + }, + plugins: [ + ...[stylePlugin(), nodeResolve(), typescript()], + ...plugins, + ], + }; +} + +function createDevConfig(mods) { + const configs = mods.map((mod) => { + return createConfigItem(mod); + }); + return configs; +} + +module.exports = createDevConfig(modules); + + + + + + + diff --git a/scripts/screen.config.js b/scripts/screen.config.js new file mode 100644 index 0000000..e6e7f6c --- /dev/null +++ b/scripts/screen.config.js @@ -0,0 +1,9 @@ +const pageList = [ + // { path: 'paint/examples/path/draw.html', w: 600, h: 600, delay: 200 }, + // { path: 'paint/examples/path/play.html', w: 600, h: 600, delay: 8000 }, + { path: 'drag-core/examples/demo.html', w: 500, h: 1000, delay: 2000 }, +] + +module.exports = { + pageList, +} \ No newline at end of file diff --git a/scripts/snapshot.js b/scripts/snapshot.js new file mode 100644 index 0000000..a9cc823 --- /dev/null +++ b/scripts/snapshot.js @@ -0,0 +1,34 @@ +const jimp = require('jimp'); +const path = require('path'); +const { delay } = require('./util/time'); +const { createScreenshot } = require('./util/screen'); +const { pageList } = require('./screen.config'); + +const snapshotDir = path.join(__dirname, '..', '__tests__', 'snapshot'); + +main(); + +async function main() { + const middlewares = []; + pageList.forEach((p) => { + middlewares.push(async (ctx = {}, next) => { + const { page, port } = ctx; + await page.setViewport( { width: p.w, height: p.h } ); + const pageUrl = `http://127.0.0.1:${port}/packages/${p.path || ''}`; + await page.goto(pageUrl); + await delay(p.delay || 100); + const buf = await page.screenshot(); + (await jimp.read(buf)).scale(1).quality(100).write(createPicPath(p.path)); + await next(); + }); + }); + await createScreenshot(middlewares, { baseDir: path.join(__dirname, '..') }); +} + + +function createPicPath(pagePath) { + let picPath = path.join(snapshotDir, pagePath); + picPath = picPath.replace(/\.html$/, '.jpg'); + return picPath; +} + diff --git a/scripts/util/dts-plugin.js b/scripts/util/dts-plugin.js new file mode 100644 index 0000000..0001ccf --- /dev/null +++ b/scripts/util/dts-plugin.js @@ -0,0 +1,55 @@ +const path = require('path'); +const fs = require('fs-extra'); +const execa = require('execa'); +const chalk = require('chalk'); +const { Extractor, ExtractorConfig } = require('@microsoft/api-extractor'); + +module.exports = function myPlugin(pkgName) { + return { + name: 'dts-plugin', // this name will show up in warnings and errors + async closeBundle() { + + // build types + const target = pkgName; + const pkgDir = path.join(__dirname, '..', '..', 'packages', `${target}`); + const pkg = require(`${pkgDir}/package.json`); + + const extractorConfigPath = path.resolve(pkgDir, `api-extractor.json`) + const extractorConfig = ExtractorConfig.loadFileAndPrepare( + extractorConfigPath + ); + const extractorResult = Extractor.invoke(extractorConfig, { + localBuild: true, + showVerboseMessages: true + }); + + if (extractorResult.succeeded) { + // concat additional d.ts to rolled-up dts + const typesDir = path.resolve(pkgDir, 'types') + if (await fs.exists(typesDir)) { + const dtsPath = path.resolve(pkgDir, pkg.types) + const existing = await fs.readFile(dtsPath, 'utf-8') + const typeFiles = await fs.readdir(typesDir) + const toAdd = await Promise.all( + typeFiles.map(file => { + return fs.readFile(path.resolve(typesDir, file), 'utf-8') + }) + ) + await fs.writeFile(dtsPath, existing + '\n' + toAdd.join('\n')) + } + console.log( + chalk.bold(chalk.green(`API Extractor completed successfully.`)) + ) + } else { + console.error( + `API Extractor completed with ${extractorResult.errorCount} errors` + + ` and ${extractorResult.warningCount} warnings` + ) + process.exitCode = 1 + } + await fs.remove(`${pkgDir}/dist/packages`) + } + }; +} + + diff --git a/scripts/util/screen.js b/scripts/util/screen.js new file mode 100644 index 0000000..d0ed5cb --- /dev/null +++ b/scripts/util/screen.js @@ -0,0 +1,60 @@ +const path = require('path'); +const http = require('http'); +const fs = require('fs'); +const puppeteer = require('puppeteer'); +const serveHandler = require('serve-handler'); +const compose = require('koa-compose'); +const { delay } = require('./time'); + +const port = 3001; +const width = 600; +const height = 600; + +module.exports = { + createScreenshotBuffer, + createScreenshot, + width, + height, +} + + +async function createScreenshotBuffer(pagePath) { + const middlewares = []; + let buf; + middlewares.push(async (ctx = {}, next) => { + const { page, port } = ctx; + await page.setViewport( { width: width, height: height } ); + const pageUrl = `http://127.0.0.1:${port}/${pagePath || ''}`; + await page.goto(pageUrl); + await delay(1000 * 2); + buf = await page.screenshot(); + }); + await createScreenshot(middlewares, { baseDir: path.join(__dirname, '..', '..') }); + return buf; +} + + +async function createScreenshot(middlewares, opts = {}) { + return new Promise((resolve, reject) => { + const server = http.createServer((req, res) => serveHandler(req, res, { + public: opts.baseDir, + })); + server.listen(port, async () => { + try { + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await compose(middlewares)({ page, port }) + await browser.close(); + server.close(); + resolve(); + } catch (err) { + server.close(); + console.error(err); + process.exit(-1); + } + }); + server.on('SIGINT', () => process.exit(1) ); + }) +} + + diff --git a/scripts/util/style-plugin.js b/scripts/util/style-plugin.js new file mode 100644 index 0000000..399c249 --- /dev/null +++ b/scripts/util/style-plugin.js @@ -0,0 +1,40 @@ +const postcss = require('rollup-plugin-postcss'); +const less = require('less'); + +module.exports = function() { + return postcss({ + extract: false, + minimize: true, + process: processLess, + }) +} + +const processLess = function(context, payload) { + return new Promise(( resolve, reject ) => { + less.render({ + file: context + }, function(err, result) { + if( !err ) { + resolve(result); + } else { + reject(err); + } + }); + + less.render(context, {}) + .then(function(output) { + // output.css = string of css + // output.map = string of sourcemap + // output.imports = array of string filenames of the imports referenced + if( output && output.css ) { + resolve(output.css); + } else { + reject({}) + } + }, + function(err) { + reject(err) + }); + + }) +} diff --git a/scripts/util/time.js b/scripts/util/time.js new file mode 100644 index 0000000..d857a91 --- /dev/null +++ b/scripts/util/time.js @@ -0,0 +1,12 @@ +module.exports = { + delay, +} + + +function delay(time = 100) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, time); + }) +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bd40edc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "declaration": true, + "outDir": "dist", + "sourceMap": false, + "target": "es5", + "module": "ES2015", + "moduleResolution": "node", + "allowJs": false, + "strict": true, + "noUnusedLocals": true, + "experimentalDecorators": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "removeComments": false, + "jsx": "preserve", + "lib": ["ESNext", "dom"], + "rootDir": ".", + // "paths": { + // "idraw": ["packages/idraw/src1"] + // } + }, + "include": [ + "packages/global.d.ts", + "packages/*/src", + "packages/*/__tests__" + ] +} \ No newline at end of file