From 23db6e4eb68ec3072e0ee6b3c2b88f8e6dcc833f Mon Sep 17 00:00:00 2001 From: Patryk Tomczyk <13100280+patzick@users.noreply.github.com> Date: Mon, 17 Aug 2020 15:37:52 +0200 Subject: [PATCH] feat(workflow): use esbuild to speed up development process (#1025) --- api-extractor.json | 2 + package.json | 10 +- packages/cli/scripts/esbuild.js | 51 +++++++++ packages/composables/package.json | 4 +- .../composables/src/hooks/useProductSearch.ts | 4 +- .../internalHelpers/associationsParameter.ts | 2 +- .../src/internalHelpers/includesParameter.ts | 2 +- .../src/internalHelpers/searchCriteria.ts | 4 +- .../composables/tsconfig-composables.json | 8 ++ packages/helpers/package.json | 4 +- packages/helpers/tsconfig-helpers.json | 8 ++ packages/nuxt-module/package.json | 7 +- packages/nuxt-module/src/packages.ts | 3 +- .../nuxt-module/tsconfig-nuxt-module.json | 8 ++ packages/shopware-6-client/package.json | 4 +- .../tsconfig-shopware-6-client.json | 8 ++ rollup.config.js | 8 +- scripts/bootstrap.js | 2 +- scripts/build.js | 106 ++++++++++++------ scripts/dev.js | 32 +++--- yarn.lock | 5 + 21 files changed, 205 insertions(+), 77 deletions(-) create mode 100644 packages/cli/scripts/esbuild.js create mode 100644 packages/composables/tsconfig-composables.json create mode 100644 packages/helpers/tsconfig-helpers.json create mode 100644 packages/nuxt-module/tsconfig-nuxt-module.json create mode 100644 packages/shopware-6-client/tsconfig-shopware-6-client.json diff --git a/api-extractor.json b/api-extractor.json index f9ddb154..3ba82d6a 100644 --- a/api-extractor.json +++ b/api-extractor.json @@ -2,6 +2,8 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "projectFolder": ".", + "apiReport": { "enabled": true, "reportFolder": "/api/" diff --git a/package.json b/package.json index ea71b3ef..1590eebf 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,10 @@ "scripts": { "start": "yarn && yarn build --ci && node scripts/init.js", "dev": "node scripts/dev.js", - "dev:client": "yarn dev shopware-6-client -format=esm-bundler", - "dev:composables": "yarn dev composables -format=esm-bundler", - "dev:helpers": "yarn dev helpers -format=esm-bundler", - "dev:nuxt-module": "yarn dev nuxt-module -format=cjs", + "dev:client": "yarn dev shopware-6-client", + "dev:composables": "yarn dev composables", + "dev:helpers": "yarn dev helpers", + "dev:nuxt-module": "yarn dev nuxt-module", "dev:debug": "node --inspect scripts/dev.js", "build": "node scripts/build.js", "postinstall": "node scripts/linkDependencies.js && lerna link", @@ -60,10 +60,12 @@ "axios": "^0.19.2", "brotli": "^1.3.2", "chalk": "^4.1.0", + "chokidar": "^3.4.2", "conventional-changelog-cli": "^2.1.0", "coveralls": "^3.1.0", "cypress": "^4.12.1", "enquirer": "^2.3.6", + "esbuild": "^0.6.22", "execa": "^4.0.3", "faker": "^4.1.0", "fs-extra": "^9.0.1", diff --git a/packages/cli/scripts/esbuild.js b/packages/cli/scripts/esbuild.js new file mode 100644 index 00000000..310c831c --- /dev/null +++ b/packages/cli/scripts/esbuild.js @@ -0,0 +1,51 @@ +const jetpack = require("fs-jetpack"); +const path = require("path"); +const { build: esBuild } = require("esbuild"); + +function getAllFiles(dirPath, arrayOfFiles = [], excludeHidden = true) { + if (!dirPath || !jetpack.exists(dirPath)) return []; + const files = jetpack.list(dirPath); + files.forEach((file) => { + if (jetpack.exists(path.join(dirPath, file)) === "dir") { + arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles); + } else { + const fileName = path.join(dirPath, file).replace(__dirname + "/", ""); + if (!(excludeHidden && file.startsWith("."))) { + arrayOfFiles.push(path.normalize(fileName)); + } + } + }); + + return arrayOfFiles; +} + +async function runBuild(packageJson) { + try { + const files = getAllFiles( + path.join(__dirname, "..", "src") + ).filter((filePath) => filePath.endsWith(".ts")); + const external = Object.keys(packageJson.dependencies); + await esBuild({ + entryPoints: files, + outdir: "build", + minify: false, + bundle: true, + external, + platform: "node", + target: "node10", + format: "cjs", + tsconfig: path.join(__dirname, "..", "tsconfig.json"), + }); + return true; + } catch (e) { + console.error("Error building CLI", e); + return false; + } +} + +async function run() { + jetpack.remove("./build"); + const pkg = require(path.join(__dirname, "..", "package.json")); + await runBuild(pkg); +} +run(); diff --git a/packages/composables/package.json b/packages/composables/package.json index e2a2fe49..814a6a92 100644 --- a/packages/composables/package.json +++ b/packages/composables/package.json @@ -3,7 +3,7 @@ "version": "0.3.1", "description": "@shopware-pwa/composables", "main": "dist/composables.cjs.js", - "module": "dist/composables.esm-bundler.js", + "module": "dist/composables.esm.js", "files": [ "dist" ], @@ -17,7 +17,7 @@ "buildOptions": { "name": "composables", "formats": [ - "esm-bundler", + "esm", "cjs" ] }, diff --git a/packages/composables/src/hooks/useProductSearch.ts b/packages/composables/src/hooks/useProductSearch.ts index f0350a4a..a47a36c6 100644 --- a/packages/composables/src/hooks/useProductSearch.ts +++ b/packages/composables/src/hooks/useProductSearch.ts @@ -1,5 +1,5 @@ import { ref, Ref, computed, reactive } from "@vue/composition-api"; -import queryString from "query-string"; +import { parse } from "query-string"; import { getSuggestedResults, getSearchResults, @@ -159,7 +159,7 @@ export const useProductSearch = ( const captureQueryParams = () => { /* istanbul ignore next */ - const criteriaQueryParams: ListingQueryParams | any = queryString.parse( + const criteriaQueryParams: ListingQueryParams | any = parse( window?.location?.search, { parseNumbers: true, diff --git a/packages/composables/src/internalHelpers/associationsParameter.ts b/packages/composables/src/internalHelpers/associationsParameter.ts index c19cf783..4983c908 100644 --- a/packages/composables/src/internalHelpers/associationsParameter.ts +++ b/packages/composables/src/internalHelpers/associationsParameter.ts @@ -1,7 +1,7 @@ /** * A collection of performance set of association parameters */ -import apiParams from "@shopware-pwa/composables/src/api-params.json"; +import * as apiParams from "@shopware-pwa/composables/src/api-params.json"; /** * Gets the right associations parameter for given entity type diff --git a/packages/composables/src/internalHelpers/includesParameter.ts b/packages/composables/src/internalHelpers/includesParameter.ts index c5ea6cc1..77668436 100644 --- a/packages/composables/src/internalHelpers/includesParameter.ts +++ b/packages/composables/src/internalHelpers/includesParameter.ts @@ -1,7 +1,7 @@ /** * A collection of performance set of includes parameters */ -import apiParams from "@shopware-pwa/composables/src/api-params.json"; +import * as apiParams from "@shopware-pwa/composables/src/api-params.json"; /** * Gets the right includes parameter for given entity type diff --git a/packages/composables/src/internalHelpers/searchCriteria.ts b/packages/composables/src/internalHelpers/searchCriteria.ts index 015e9f62..9b73f5f5 100644 --- a/packages/composables/src/internalHelpers/searchCriteria.ts +++ b/packages/composables/src/internalHelpers/searchCriteria.ts @@ -1,4 +1,4 @@ -import queryString from "query-string"; +import { stringify } from "query-string"; import { ListingQueryParams, SearchCriteria, @@ -26,7 +26,7 @@ export function appendSearchCriteriaToUrl( manufacturer: manufacturer, properties: properties, }; - const combinedURL = queryString.stringify(query, { + const combinedURL = stringify(query, { arrayFormat: "separator", arrayFormatSeparator: "|", skipNull: true, diff --git a/packages/composables/tsconfig-composables.json b/packages/composables/tsconfig-composables.json new file mode 100644 index 00000000..1f423efb --- /dev/null +++ b/packages/composables/tsconfig-composables.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "files": ["src/index.ts"], + "compilerOptions": { + "outDir": "./dist/" + } +} diff --git a/packages/helpers/package.json b/packages/helpers/package.json index 3d45beba..f0a11b34 100644 --- a/packages/helpers/package.json +++ b/packages/helpers/package.json @@ -3,7 +3,7 @@ "version": "0.3.1", "description": "@shopware-pwa/helpers", "main": "dist/helpers.cjs.js", - "module": "dist/helpers.esm-bundler.js", + "module": "dist/helpers.esm.js", "files": [ "dist" ], @@ -17,7 +17,7 @@ "buildOptions": { "name": "helpers", "formats": [ - "esm-bundler", + "esm", "cjs" ] }, diff --git a/packages/helpers/tsconfig-helpers.json b/packages/helpers/tsconfig-helpers.json new file mode 100644 index 00000000..1f423efb --- /dev/null +++ b/packages/helpers/tsconfig-helpers.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "files": ["src/index.ts"], + "compilerOptions": { + "outDir": "./dist/" + } +} diff --git a/packages/nuxt-module/package.json b/packages/nuxt-module/package.json index f5abef8c..969281e8 100644 --- a/packages/nuxt-module/package.json +++ b/packages/nuxt-module/package.json @@ -3,7 +3,6 @@ "version": "0.3.1", "description": "@shopware-pwa/nuxt-module", "main": "dist/nuxt-module.cjs.js", - "module": "dist/nuxt-module.esm-bundler.js", "files": [ "dist", "plugins/*" @@ -36,6 +35,12 @@ "publishConfig": { "access": "public" }, + "buildOptions": { + "name": "nuxt-module", + "formats": [ + "cjs" + ] + }, "scripts": { "snyk-protect": "snyk protect" }, diff --git a/packages/nuxt-module/src/packages.ts b/packages/nuxt-module/src/packages.ts index 02c2401e..fc038a35 100644 --- a/packages/nuxt-module/src/packages.ts +++ b/packages/nuxt-module/src/packages.ts @@ -1,4 +1,5 @@ import path from "path"; +import jetpack from "fs-jetpack"; import { NuxtModuleOptions, WebpackConfig } from "./interfaces"; /* istanbul ignore next */ @@ -8,7 +9,7 @@ export function useCorePackages( ) { const useRawSource = (packageName: string) => { const pkgPath = path.resolve(path.join("node_modules", packageName)); - const pkg = require(path.join(pkgPath, "package.json")); + const pkg = jetpack.read(path.join(pkgPath, "package.json"), "json"); if (pkg.module) { moduleObject.extendBuild((config: WebpackConfig) => { diff --git a/packages/nuxt-module/tsconfig-nuxt-module.json b/packages/nuxt-module/tsconfig-nuxt-module.json new file mode 100644 index 00000000..1f423efb --- /dev/null +++ b/packages/nuxt-module/tsconfig-nuxt-module.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "files": ["src/index.ts"], + "compilerOptions": { + "outDir": "./dist/" + } +} diff --git a/packages/shopware-6-client/package.json b/packages/shopware-6-client/package.json index 82f18f04..50029fff 100644 --- a/packages/shopware-6-client/package.json +++ b/packages/shopware-6-client/package.json @@ -3,7 +3,7 @@ "version": "0.3.1", "description": "Rest API client for Shopware 6.", "main": "dist/shopware-6-client.cjs.js", - "module": "dist/shopware-6-client.esm-bundler.js", + "module": "dist/shopware-6-client.esm.js", "files": [ "dist" ], @@ -17,7 +17,7 @@ "buildOptions": { "name": "Shopware6Client", "formats": [ - "esm-bundler", + "esm", "cjs" ] }, diff --git a/packages/shopware-6-client/tsconfig-shopware-6-client.json b/packages/shopware-6-client/tsconfig-shopware-6-client.json new file mode 100644 index 00000000..1f423efb --- /dev/null +++ b/packages/shopware-6-client/tsconfig-shopware-6-client.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "files": ["src/index.ts"], + "compilerOptions": { + "outDir": "./dist/" + } +} diff --git a/rollup.config.js b/rollup.config.js index 5115b202..05f58d45 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -19,8 +19,8 @@ const packageOptions = pkg.buildOptions || {}; let hasTSChecked = false; const outputConfigs = { - "esm-bundler": { - file: resolve(`dist/${name}.esm-bundler.js`), + esm: { + file: resolve(`dist/${name}.esm.js`), format: `es`, }, cjs: { @@ -37,7 +37,7 @@ const outputConfigs = { }, }; -const defaultFormats = ["esm-bundler", "cjs"]; +const defaultFormats = ["esm", "cjs"]; const inlineFormats = process.env.FORMATS && process.env.FORMATS.split(","); const packageFormats = inlineFormats || packageOptions.formats || defaultFormats; @@ -72,7 +72,7 @@ function createConfig(format, output, plugins = []) { const isGlobalBuild = /global/.test(format); const isRawESMBuild = format === "esm"; const isNodeBuild = format === "cjs"; - const isBundlerESMBuild = /esm-bundler/.test(format); + const isBundlerESMBuild = /esm/.test(format); if (isGlobalBuild) { output.name = packageOptions.name; diff --git a/scripts/bootstrap.js b/scripts/bootstrap.js index 8a8d9749..2795b4b6 100644 --- a/scripts/bootstrap.js +++ b/scripts/bootstrap.js @@ -29,7 +29,7 @@ files.forEach((shortName) => { version: baseVersion, description: name, main: `dist/${shortName}.cjs.js`, - module: `dist/${shortName}.esm-bundler.js`, + module: `dist/${shortName}.esm.js`, files: [`dist`], types: `dist/${shortName}.d.ts`, unpkg: `dist/${shortName}.global.js`, diff --git a/scripts/build.js b/scripts/build.js index e33c2785..d8dd4301 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -25,18 +25,16 @@ const { allTargets, fuzzyMatchTarget, } = require("./utils"); +const { build: esBuild } = require("esbuild"); const args = require("minimist")(process.argv.slice(2)); const isCIRun = !!args.ci; const targets = args._; const formats = args.formats || args.f; const devOnly = args.devOnly || args.d; -const prodOnly = !devOnly && (args.prodOnly || args.p); -const sourceMap = args.sourcemap || args.s; const isRelease = args.release; -const buildTypes = true; // args.t || args.types || isRelease || isCIRun; -> for now build always with types +const buildTypes = args.t || args.types || isRelease || isCIRun; const buildAllMatching = args.all || args.a; -const commit = execa.sync("git", ["rev-parse", "HEAD"]).stdout.slice(0, 7); run(); @@ -80,21 +78,50 @@ async function buildAll(targets) { } } +async function runBuild({ target, packageJson, format = "esm" }) { + const buildTarget = format === "esm" ? "es2020" : "es2015"; + + try { + const external = Object.keys(packageJson.dependencies); + await esBuild({ + entryPoints: [path.join("packages", target, "src", "index.ts")], + outfile: path.join("packages", target, "dist", `${target}.${format}.js`), + minify: false, + bundle: true, + external, + target: buildTarget, + format, + }); + return true; + } catch (e) { + console.error("Error building " + target, e); + return false; + } +} + +async function buildPackage(target, packageJson) { + const formats = + (packageJson.buildOptions && packageJson.buildOptions.formats) || []; + const results = await Promise.all( + formats.map((format) => { + return runBuild({ + target, + packageJson, + format, + }); + }) + ); + return !results.includes(false); +} + async function build(target) { + console.log(); + console.log(`Building ${chalk.bold(target)} package...`); + const { performance } = require("perf_hooks"); + const t0 = performance.now(); const pkgDir = path.resolve(`packages/${target}`); const pkg = require(`${pkgDir}/package.json`); - // only build published packages for release - if (isRelease && pkg.private) { - if (isCIRun) { - await execa("npx", ["yalc", "push"], { - stdio: "inherit", - cwd: pkgDir, - }); - } - return; - } - // run custom package build if there is one if (pkg.scripts && pkg.scripts.build) { await execa("yarn", ["build"], { stdio: "inherit", cwd: pkgDir }); @@ -106,28 +133,10 @@ async function build(target) { await fs.remove(`${pkgDir}/dist`); } - const env = - (pkg.buildOptions && pkg.buildOptions.env) || - (devOnly ? "development" : "production"); - await execa( - "rollup", - [ - "-c", - "--environment", - [ - `COMMIT:${commit}`, - `NODE_ENV:${env}`, - `TARGET:${target}`, - formats ? `FORMATS:${formats}` : ``, - buildTypes ? `TYPES:true` : ``, - prodOnly ? `PROD_ONLY:true` : ``, - sourceMap ? `SOURCE_MAP:true` : ``, - ] - .filter(Boolean) - .join(","), - ], - { stdio: "inherit" } - ); + const buildResult = await buildPackage(target, pkg); + if (!buildResult) return false; + const t1 = performance.now(); + console.log(chalk.green(`Build success: ${Math.round(t1 - t0)} ms`)); if (buildTypes && pkg.types) { console.log(); @@ -135,6 +144,26 @@ async function build(target) { chalk.bold(chalk.yellow(`Rolling up type definitions for ${target}...`)) ); + try { + await execa( + "yarn", + [ + "tsc", + "--emitDeclarationOnly", + "--declaration", + "--skipLibCheck", + "--project", + path.join("packages", target, `tsconfig-${target}.json`), + ], + { + stdio: "inherit", + } + ); + } catch (e) { + console.error("Problem with generationg declarations file", e); + return false; + } + // build types const { Extractor, ExtractorConfig } = require("@microsoft/api-extractor"); @@ -180,6 +209,9 @@ async function build(target) { await fs.remove(`${pkgDir}/dist/packages`); } + const t2 = performance.now(); + console.log(chalk.green(`Whole package build: ${Math.round(t2 - t0)} ms`)); + console.log(); } function checkAllSizes(targets) { diff --git a/scripts/dev.js b/scripts/dev.js index a1123c87..3a08d314 100644 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -17,26 +17,24 @@ __DEV__=false yarn dev */ const execa = require("execa"); +const path = require("path"); const { fuzzyMatchTarget } = require("./utils"); const args = require("minimist")(process.argv.slice(2)); const target = args._.length ? fuzzyMatchTarget(args._)[0] : "shopware-6-client"; -const formats = args.formats || args.f; -const commit = execa.sync("git", ["rev-parse", "HEAD"]).stdout.slice(0, 7); +const chokidar = require("chokidar"); -execa( - "rollup", - [ - "-wc", - "--environment", - [ - `COMMIT:${commit}`, - `TARGET:${target}`, - `FORMATS:${formats || "cjs"}` - ].join(",") - ], - { - stdio: "inherit" - } -); +chokidar + .watch([path.join(__dirname, "..", "packages", target, "src")], { + ignoreInitial: true, + }) + .on("all", async (event) => { + execa("yarn", ["build", target], { + stdio: "inherit", + }); + }); + +execa("yarn", ["build", target], { + stdio: "inherit", +}); diff --git a/yarn.lock b/yarn.lock index abb83565..960ddd37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8548,6 +8548,11 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +esbuild@^0.6.22: + version "0.6.22" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.6.22.tgz#cadabf5d8762d105206c501a31c2c0afe6b0ffd3" + integrity sha512-dgcdoOZ7rghMageuGFS6geQ77UacoZ/qrd0MRc2iPnYtMNfQ1cJ7mVkotVpUDJYdOWvLVa87NFEtqPg7D2mKSA== + escape-goat@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"