From ae719fde9ef0ccaedcee4ede9c5ff323bdbb58bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Mon, 11 Mar 2024 11:08:32 +0800 Subject: [PATCH 01/87] switching to lincd 1.0 cleaning up defaults around env variables and run scripts. lincd-cli should only contain things that everyone using lincd is interested in, not semantu specific preferences TODO: update enable capacitor script to merge env variables and run scripts from templates --- defaults/app-static/.env-cmdrc.json | 17 +++++++++ defaults/app-static/package.json | 8 +++++ defaults/app-with-backend/.env-cmdrc.json | 5 +-- defaults/app-with-backend/package.json | 13 +++---- defaults/app-with-backend/scripts/build.js | 41 ---------------------- defaults/package/package.json | 2 +- package.json | 2 +- src/cli-methods.ts | 2 +- 8 files changed, 33 insertions(+), 57 deletions(-) create mode 100644 defaults/app-static/.env-cmdrc.json create mode 100644 defaults/app-static/package.json delete mode 100644 defaults/app-with-backend/scripts/build.js diff --git a/defaults/app-static/.env-cmdrc.json b/defaults/app-static/.env-cmdrc.json new file mode 100644 index 0000000..eadfc4f --- /dev/null +++ b/defaults/app-static/.env-cmdrc.json @@ -0,0 +1,17 @@ +{ + "app-main": { + "APP_ID": "com.yourdomain.www", + "APP_ENV": true, + "OUTPUT_PATH": "./web/assets", + "ASSET_PATH": "./assets/", + "ENTRY_PATH": "./src/index-static.tsx" + }, + "app-local-android": { + "NODE_ENV": "app", + "SITE_ROOT": "http://10.0.2.2:4000" + }, + "app-local-ios": { + "NODE_ENV": "app", + "SITE_ROOT": "http://localhost:4000" + } +} \ No newline at end of file diff --git a/defaults/app-static/package.json b/defaults/app-static/package.json new file mode 100644 index 0000000..2ea7678 --- /dev/null +++ b/defaults/app-static/package.json @@ -0,0 +1,8 @@ +{ + "scripts": { + "app": "env-cmd -e _main,production,app-main node scripts/build.js && npx cap sync", + "app-local-ios": "env-cmd -e _main,development,app-main,app-local-ios node scripts/build.js && npx cap sync", + "app-local-android": "env-cmd -e _main,development,app-main,app-local-android node scripts/build.js && npx cap sync", + "app-staging": "env-cmd -e _main,staging,app-main,app-staging node scripts/build.js && npx cap sync" + } +} \ No newline at end of file diff --git a/defaults/app-with-backend/.env-cmdrc.json b/defaults/app-with-backend/.env-cmdrc.json index 732e517..2cb50e6 100644 --- a/defaults/app-with-backend/.env-cmdrc.json +++ b/defaults/app-with-backend/.env-cmdrc.json @@ -9,11 +9,8 @@ "NODE_ENV": "development", "SITE_ROOT": "http://localhost:4000" }, - "staging": { - "NODE_ENV": "staging", - "SITE_ROOT": "https://next.${hyphen_name}.com" - }, "production": { + "PORT": "4000", "NODE_ENV": "production", "SITE_ROOT": "https://app.${hyphen_name}.com" } diff --git a/defaults/app-with-backend/package.json b/defaults/app-with-backend/package.json index 283e101..a25fbca 100644 --- a/defaults/app-with-backend/package.json +++ b/defaults/app-with-backend/package.json @@ -16,17 +16,12 @@ "main": "scripts/start-server.js", "license": "MIT", "scripts": { - "start": "npm run server:dev", - "build": "env-cmd -e _main,production node scripts/build.js", - "build:staging": "env-cmd -e _main,staging node scripts/build.js", - "deploy": "git stash && git pull && yarn mrgit exec \"git stash\" && env-cmd --use-shell \"yarn checkout && yarn dedupe && semantu build-workspace -ug && yarn build && pm2 restart ${hyphen_name}\"", - "deploy:staging": "git stash && git pull && yarn mrgit exec \"git stash\" && env-cmd --use-shell \"yarn checkout && yarn dedupe && semantu build-workspace -ug && yarn build-staging && pm2 restart ${hyphen_name}-staging\"", + "start": "yarn lincd start --env development", + "build": "yarn lincd build-app --env production", "postinstall": "husky install", "prepack": "pinst --disable", "postpack": "pinst --enable", - "server:dev": "env-cmd -e _main,development nodemon --quiet --watch ./src/index.tsx ./scripts/start-server.js", - "server:prod": "env-cmd -e _main,production node ./scripts/start-server.js", - "server:staging": "env-cmd -e _main,staging node ./scripts/start-server.js", + "server:prod": "yarn lincd start --env production", "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"", "lint:fix": "yarn lint --fix", "prettier": "prettier \"src/**/*.{js,jsx,ts,tsx,css,scss}\" --check", @@ -63,7 +58,7 @@ "@babel/preset-typescript": "^7.22.5", "@babel/register": "^7.22.5", "chalk": "^4.1.2", - "lincd": "^0.5.22", + "lincd": "^1.0", "lincd-auth": "^0.1.19", "lincd-jsonld": "^0.1.21", "lincd-server": "^0.1.39", diff --git a/defaults/app-with-backend/scripts/build.js b/defaults/app-with-backend/scripts/build.js deleted file mode 100644 index b185ebe..0000000 --- a/defaults/app-with-backend/scripts/build.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; -const webpack = require('webpack'); -const config = require('lincd-server/site.webpack.config'); -const chalk = require('chalk'); -const { buildMetadata } = require('lincd-cli/lib/cli-methods'); - -webpack(config, async (err, stats) => { - if (err) { - console.error(err.stack || err); - if (err.details) { - console.error(err.details); - } - process.exit(1); - } - const info = stats.toJson(); - if (stats.hasErrors()) { - console.log('Finished running webpack with errors.'); - info.errors.forEach((e) => console.error(e)); - process.exit(1); - } else { - console.log( - stats.toString({ - chunks: false, - assets: true, - entryPoints: false, - modules: false, - moduleAssets: false, - moduleChunks: false, - colors: true, - }), - ); - // console.log( - // chalk.green('\t'+Object.keys(stats.compilation.assets).join('\n\t')), - // ); - - //build metadata (JSON-LD files containing metadata about the lincd components, shapes & ontologies in this app or its packages) - // let updatedPaths = await buildMetadata(); - // console.log(chalk.green("Updated metadata:\n")+" - "+updatedPaths.map(p => chalk.magenta(p.replace(process.cwd(),''))).join("\n - ")); - } - process.exit(); -}); diff --git a/defaults/package/package.json b/defaults/package/package.json index 2a9c988..4eed609 100644 --- a/defaults/package/package.json +++ b/defaults/package/package.json @@ -29,7 +29,7 @@ "web3" ], "dependencies": { - "lincd": "^0.5", + "lincd": "^1.0", "lincd-jsonld": "^0.1" }, "devDependencies": { diff --git a/package.json b/package.json index 4f36e31..ff1acfd 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "grunt-ts": "^6.0.0-beta.22", "grunt-webpack": "^6.0.0", "license-info-webpack-plugin": "^3.0.0", - "lincd": "^0.6", + "lincd": "^1.0", "lincd-jsonld": "^0.1.22", "lincd-modules": "^0.1", "load-grunt-tasks": "^5.1.0", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 9161156..5767b09 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -21,7 +21,7 @@ import {findNearestPackageJson} from 'find-nearest-package-json'; import {GetEnvVars} from 'env-cmd'; // const config = require('lincd-server/site.webpack.config'); -var glob = require('glob'); +var {glob} = require('glob'); var variables = {}; var open = require('open'); var stagedGitFiles = require('staged-git-files'); From eefb122349a3c3121be5b08439c1efcd89a29e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Wed, 3 Apr 2024 15:30:35 +0800 Subject: [PATCH 02/87] turned metadata off and switched to lincd 1.0 --- defaults/app-static/src/index-static.tsx | 8 ++++---- .../{setup_storage.js => storage-config.js} | 4 ++-- defaults/app-with-backend/src/index.tsx | 2 +- defaults/app-with-backend/src/package.ts | 2 +- .../package/src/ontologies/example-ontology.ts | 4 ++-- defaults/package/src/package.ts | 15 ++++++++++++--- defaults/shape.ts | 4 ++-- src/cli.ts | 5 +++-- src/index.ts | 2 +- src/metadata.ts | 8 ++++---- src/plugins/externalise-modules.ts | 2 +- 11 files changed, 33 insertions(+), 23 deletions(-) rename defaults/app-with-backend/scripts/{setup_storage.js => storage-config.js} (66%) diff --git a/defaults/app-static/src/index-static.tsx b/defaults/app-static/src/index-static.tsx index 86774c0..a79e146 100644 --- a/defaults/app-static/src/index-static.tsx +++ b/defaults/app-static/src/index-static.tsx @@ -3,11 +3,11 @@ import ReactDOM from 'react-dom/client'; import {BrowserRouter} from 'react-router-dom'; import App from './App'; import {BackendAPIStore} from 'lincd-server/lib/shapes/BackendAPIStore'; -import {LinkedStorage} from 'lincd/lib/utils/LinkedStorage'; +import {LinkedStorage} from 'lincd/utils/LinkedStorage'; import {AppContextProvider} from 'lincd-server-utils/lib/components/AppContext'; const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement + document.getElementById('root') as HTMLElement, ); //a BackendAPIStore is the default setup //it forwards all storage requests to a backend server @@ -21,5 +21,5 @@ root.render( - -); \ No newline at end of file + , +); diff --git a/defaults/app-with-backend/scripts/setup_storage.js b/defaults/app-with-backend/scripts/storage-config.js similarity index 66% rename from defaults/app-with-backend/scripts/setup_storage.js rename to defaults/app-with-backend/scripts/storage-config.js index 582cc44..27c0cb9 100644 --- a/defaults/app-with-backend/scripts/setup_storage.js +++ b/defaults/app-with-backend/scripts/storage-config.js @@ -1,6 +1,6 @@ const { NodeFileStore } = require('lincd-server/lib/shapes/NodeFileStore'); -const { Storage } = require('lincd/lib/utils/Storage'); +const { LinkedStorage } = require('lincd/utils/LinkedStorage'); //on the backend, we use a file store which stores all data as JSON-LD let fileStore = new NodeFileStore(process.env.NODE_ENV + '-main'); -Storage.setDefaultStore(fileStore); +LinkedStorage.setDefaultStore(fileStore); diff --git a/defaults/app-with-backend/src/index.tsx b/defaults/app-with-backend/src/index.tsx index 753f974..a099a73 100644 --- a/defaults/app-with-backend/src/index.tsx +++ b/defaults/app-with-backend/src/index.tsx @@ -3,7 +3,7 @@ import { hydrateRoot } from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import App from './App'; import React from 'react'; -import { LinkedStorage } from 'lincd/lib/utils/LinkedStorage'; +import { LinkedStorage } from 'lincd/utils/LinkedStorage'; import { BackendAPIStore } from 'lincd-server/lib/shapes/BackendAPIStore'; import { AppContextProvider } from 'lincd-server-utils/lib/components/AppContext'; diff --git a/defaults/app-with-backend/src/package.ts b/defaults/app-with-backend/src/package.ts index 1d3b26a..ebdab68 100644 --- a/defaults/app-with-backend/src/package.ts +++ b/defaults/app-with-backend/src/package.ts @@ -1,4 +1,4 @@ -import {linkedPackage} from 'lincd/lib/utils/Module'; +import { linkedPackage } from 'lincd/utils/Package'; export const { linkedComponent, diff --git a/defaults/package/src/ontologies/example-ontology.ts b/defaults/package/src/ontologies/example-ontology.ts index f628a95..cf6f999 100644 --- a/defaults/package/src/ontologies/example-ontology.ts +++ b/defaults/package/src/ontologies/example-ontology.ts @@ -1,6 +1,6 @@ -import {NamedNode} from 'lincd/lib/models'; +import {NamedNode} from 'lincd/models'; import {JSONLD} from 'lincd-jsonld/lib/utils/JSONLD'; -import {createNameSpace} from 'lincd/lib/utils/NameSpace'; +import {createNameSpace} from 'lincd/utils/NameSpace'; import {linkedOntology} from '../package'; //import all the exports of this file as one variable called _this (we need this at the end) import * as _this from './${hyphen_name}'; diff --git a/defaults/package/src/package.ts b/defaults/package/src/package.ts index 870dda1..0fa63ee 100644 --- a/defaults/package/src/package.ts +++ b/defaults/package/src/package.ts @@ -1,4 +1,13 @@ -import {linkedPackage} from 'lincd/lib/utils/Module'; +import {linkedPackage} from 'lincd/utils/Package'; -export const {linkedComponent, linkedComponentClass, linkedShape, linkedUtil, linkedOntology, registerPackageModule, registerPackageExport, packageExports, packageName} = - linkedPackage('${package_name}'); +export const { + linkedComponent, + linkedComponentClass, + linkedShape, + linkedUtil, + linkedOntology, + registerPackageModule, + registerPackageExport, + packageExports, + packageName, +} = linkedPackage('${package_name}'); diff --git a/defaults/shape.ts b/defaults/shape.ts index 165c4db..99db700 100644 --- a/defaults/shape.ts +++ b/defaults/shape.ts @@ -1,5 +1,5 @@ -import {Shape} from 'lincd/lib/shapes/Shape'; -import {NamedNode} from 'lincd/lib/models'; +import {Shape} from 'lincd/shapes/Shape'; +import {NamedNode} from 'lincd/models'; import {linkedShape} from '../package'; @linkedShape diff --git a/src/cli.ts b/src/cli.ts index 304039d..1d50672 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -25,7 +25,7 @@ import { register, startServer, } from './cli-methods'; -import {buildMetadata} from './metadata'; +// import {buildMetadata} from './metadata'; require('require-extensions'); var program = require('commander'); @@ -166,7 +166,8 @@ program }); program.command('build-metadata').action(() => { - buildMetadata(); + console.log('Needs to be reimplemented'); + // buildMetadata(); }); program .command('build-app') diff --git a/src/index.ts b/src/index.ts index 8b10e02..6af2e29 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ export {default as DeclarationPlugin} from './plugins/declaration-plugin'; export {default as externaliseModules} from './plugins/externalise-modules'; export {default as checkImports} from './plugins/check-imports'; export {default as generateGruntConfig} from './config-grunt'; -export {buildMetadata} from './metadata'; +// export {buildMetadata} from './metadata'; import {generateWebpackConfig} from './config-webpack'; export {generateWebpackConfig}; diff --git a/src/metadata.ts b/src/metadata.ts index b219f1b..08eb07e 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -1,12 +1,12 @@ -import {NamedNode} from 'lincd/lib/models'; +import {NamedNode} from 'lincd/models'; import {GetEnvVars} from 'env-cmd'; import path from 'path'; import fs from 'fs-extra'; -import {createNameSpace} from 'lincd/lib/utils/NameSpace'; -import {Prefix} from 'lincd/lib/utils/Prefix'; -import {JSONLDWriter} from 'lincd-jsonld/lib/utils/JSONLDWriter'; +import {createNameSpace} from 'lincd/utils/NameSpace'; +import {Prefix} from 'lincd/utils/Prefix'; import {getPackageJSON} from './utils'; import {warn} from './cli-methods'; +import {JSONLDWriter} from 'lincd-jsonld/lib/utils/JSONLDWriter'; function getLocalPackagePaths() { let packagePaths = []; diff --git a/src/plugins/externalise-modules.ts b/src/plugins/externalise-modules.ts index 8b411a3..fd9fb5a 100644 --- a/src/plugins/externalise-modules.ts +++ b/src/plugins/externalise-modules.ts @@ -135,7 +135,7 @@ var externaliseModules = function (config, es5) { return callback(); } - //remove export root path (for example with lincd/lib/models: the module has lib/ as root, so to get to the exported path lincd.models we need to remove it) + //remove export root path (for example with lincd/models: the module has lib/ as root, so to get to the exported path lincd.models we need to remove it) let cleanRequest = request.replace(exportRoot, ''); let targetVariable; From c905b783a9df78206db02178fd200cb4a0917794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Fri, 5 Apr 2024 10:44:40 +0800 Subject: [PATCH 03/87] fix env variables --- src/cli-methods.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 5767b09..d3ef234 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -1181,17 +1181,19 @@ export const ensureEnvironmentLoaded = async () => { if (args.includes('--env')) { let envIndex = args.indexOf('--env'); let env = args[envIndex + 1]; - if (environments.includes(env)) { - console.log('Environment: ' + env); - process.env = {...process.env, ...vars[env]}; - } else { - console.warn( - 'Environment ' + - env + - ' not found in .env-cmdrc.json. Available environments: ' + - environments.join(', '), - ); - } + env.split(',').forEach((singleEnvironment) => { + if (environments.includes(singleEnvironment)) { + console.log('Environment: ' + singleEnvironment); + process.env = {...process.env, ...vars[singleEnvironment]}; + } else { + console.warn( + 'Environment ' + + singleEnvironment + + ' not found in .env-cmdrc.json. Available environments: ' + + environments.join(', '), + ); + } + }); } else { //chose development by default process.env = {...process.env, ...vars.development}; From 2e9e3c12ca14a0dc9d9a8e2f6b27a753eda71385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Mon, 15 Apr 2024 08:03:54 +0800 Subject: [PATCH 04/87] rename variable --- defaults/app-with-backend/scripts/storage-config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/defaults/app-with-backend/scripts/storage-config.js b/defaults/app-with-backend/scripts/storage-config.js index 27c0cb9..01cc1c2 100644 --- a/defaults/app-with-backend/scripts/storage-config.js +++ b/defaults/app-with-backend/scripts/storage-config.js @@ -2,5 +2,5 @@ const { NodeFileStore } = require('lincd-server/lib/shapes/NodeFileStore'); const { LinkedStorage } = require('lincd/utils/LinkedStorage'); //on the backend, we use a file store which stores all data as JSON-LD -let fileStore = new NodeFileStore(process.env.NODE_ENV + '-main'); -LinkedStorage.setDefaultStore(fileStore); +let quadStore = new NodeFileStore(process.env.NODE_ENV + '-main'); +LinkedStorage.setDefaultStore(quadStore); From 8d61d2bfb8949b9588e122649cb795efc304df2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Wed, 17 Apr 2024 15:20:47 +0800 Subject: [PATCH 05/87] Towards ESM support across all Lincd packages lincd CLI is now itself a dual package we use tsx instead of @babel/register all require statements became import statements grunt is still broken --- expose-grunt.js => expose-grunt.cjs | 0 package.json | 38 +- src/cli-methods.ts | 50 +- src/cli.ts | 40 +- src/{config-grunt.ts => config-grunt.cts} | 21 +- src/config-webpack-app.ts | 528 +++++++++++----------- src/config-webpack.ts | 28 +- src/index.ts | 2 +- src/plugins/declaration-plugin.ts | 12 +- src/plugins/externalise-modules.ts | 5 +- src/utils.ts | 8 +- tsconfig-cjs.json | 15 + tsconfig-esm.json | 16 + tsconfig.json | 1 + 14 files changed, 421 insertions(+), 343 deletions(-) rename expose-grunt.js => expose-grunt.cjs (100%) rename src/{config-grunt.ts => config-grunt.cts} (97%) create mode 100644 tsconfig-cjs.json create mode 100644 tsconfig-esm.json diff --git a/expose-grunt.js b/expose-grunt.cjs similarity index 100% rename from expose-grunt.js rename to expose-grunt.cjs diff --git a/package.json b/package.json index f7f3827..869b1ad 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,36 @@ "name": "lincd-cli", "version": "1.1.1", "description": "Command line tools for the lincd.js library", - "main": "lib/index.js", + "main": "lib/cjs/index.js", + "module": "lib/esm/index.js", + "exports": { + ".": { + "types": "./lib/esm/index.d.ts", + "import": "./lib/esm/index.js", + "require": "./lib/cjs/index.js" + }, + "./*": { + "types": "./lib/esm/*.d.ts", + "import": "./lib/esm/*.js", + "require": "./lib/cjs/*.js" + } + }, + "typesVersions": { + "*": { + "*": [ + "lib/esm/*" + ] + } + }, "scripts": { - "prepublishOnly": "npm exec tsc", - "build": "npm exec tsc", + "prepublishOnly": "npm exec build", + "build": "rimraf ./lib && echo \"compiling CJS\" && yarn tsc -p tsconfig-cjs.json && echo \"compiling ESM\" && yarn tsc -p tsconfig-esm.json && yarn dual-package", + "dual-package": "echo 'adding package.json to each lib folder for dual output support' && yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json", + "dev": "yarn tsc -p tsconfig-esm.json -w", + "dev-cjs": "yarn tsc -p tsconfig-cjs.json -w", "postinstall": "husky install", "prepack": "npm exec tsc && pinst --disable && yarn version patch", "postpack": "pinst --enable", - "dev": "npm exec tsc -- -w", "prettier": "prettier \"src/**/*.{js,jsx,ts,tsx,css,scss}\" --check", "prettier:fix": "yarn prettier --write", "format": "yarn prettier:fix && yarn lint:fix" @@ -36,9 +58,9 @@ }, "license": "MPL-2.0", "bin": { - "grunt": "expose-grunt.js", - "lincd": "lib/cli.js", - "lincd-cli": "lib/cli.js" + "grunt": "expose-grunt.cjs", + "lincd": "lib/esm/cli.js", + "lincd-cli": "lib/esm/cli.js" }, "dependencies": { "@babel/cli": "^7.23.9", @@ -99,6 +121,8 @@ "terser-webpack-plugin": "^5.3.10", "ts-loader": "^9.5.1", "tsconfig-paths-webpack-plugin": "^4.1.0", + "tsconfig-to-dual-package": "^1.2.0", + "tsx": "^4.7.2", "typescript": "^4.8.4", "webpack": "^5.90.3", "webpack-bundle-analyzer": "^4.10.1", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 7c93334..01c3a87 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; import {exec} from 'child_process'; import depcheck from 'depcheck'; -import {getEnvFile} from 'env-cmd/dist/get-env-vars'; +import {getEnvFile} from 'env-cmd/dist/get-env-vars.js'; import fs from 'fs-extra'; import path from 'path'; import { @@ -14,22 +14,22 @@ import { getPackageJSON, isValidLINCDImport, needsRebuilding, -} from './utils'; +} from './utils.js'; import {statSync} from 'fs'; import {PackageDetails} from 'interfaces'; import {findNearestPackageJson} from 'find-nearest-package-json'; -import {GetEnvVars} from 'env-cmd'; -import {LinkedFileStorage} from 'lincd/lib/utils/LinkedFileStorage'; -// const config = require('lincd-server/site.webpack.config'); +import {LinkedFileStorage} from 'lincd/utils/LinkedFileStorage'; +// import pkg from 'lincd/utils/LinkedFileStorage'; +// const { LinkedFileStorage } = pkg; -var {glob} = require('glob'); -var variables = {}; -var open = require('open'); -var stagedGitFiles = require('staged-git-files'); +// const config = require('lincd-server/site.webpack.config'); +import { glob } from 'glob'; +import webpack from 'webpack'; -const webpack = require('webpack'); +import stagedGitFiles from 'staged-git-files'; +var variables = {}; export const createApp = async (name, basePath = process.cwd()) => { if (!name) { console.warn('Please provide a name as the first argument'); @@ -830,12 +830,12 @@ const replaceVariablesInFiles = function (...files: string[]) { const replaceVariablesInFolder = function (folder: string) { //get all files in folder, including files that start with a dot - glob(folder + '/**/*', {dot: true, nodir: true}, function (err, files) { + (glob as any)(folder + '/**/*', {dot: true, nodir: true}, async function (err, files) { if (err) { console.log('Error', err); } else { // console.log(files); - return Promise.all( + await Promise.all( files.map((file) => { return replaceVariablesInFile(file); }), @@ -1159,7 +1159,7 @@ export const depCheck = async (path: string = process.cwd()) => { export const ensureEnvironmentLoaded = async () => { if (!process.env.NODE_ENV) { //load env-cmd for development environment - let {GetEnvVars} = require('env-cmd'); + let {GetEnvVars} = await import('env-cmd'); let envCmdrcPath = path.join(process.cwd(), '.env-cmdrc.json'); if (!fs.existsSync(envCmdrcPath)) { console.warn( @@ -1209,16 +1209,16 @@ export const startServer = async ( ) => { await ensureEnvironmentLoaded(); - let lincdConfig = require(path.join(process.cwd(), 'lincd.config')); + let lincdConfig = await import(path.join(process.cwd(), 'lincd.config.js')); if (!ServerClass) { - ServerClass = require('lincd-server/lib/shapes/LincdServer').LincdServer; + ServerClass = (await import('lincd-server/lib/shapes/LincdServer.js')).LincdServer; } - require(path.join(process.cwd(), 'scripts', 'storage-config')); + await import(path.join(process.cwd(), 'scripts', 'storage-config.js')); let server = new ServerClass({ - loadAppComponent: () => - require(path.join(process.cwd(), 'src', 'App')).default, + loadAppComponent: async () => + (await import(path.join(process.cwd(), 'src', 'App'))).default, ...lincdConfig, }); //Important to use slice, because when using clusers, child processes need to be able to read the same arguments @@ -1232,16 +1232,13 @@ export const startServer = async ( }; export const buildApp = async () => { await ensureEnvironmentLoaded(); - const webpackAppConfig = require('./config-webpack-app').webpackAppConfig; + const webpackAppConfig = await (await import('./config-webpack-app.js')).getWebpackAppConfig(); console.log(chalk.magenta(`Building ${process.env.NODE_ENV} app bundles`)); return new Promise((resolve, reject) => { - webpack(webpackAppConfig, async (err, stats) => { + webpack(webpackAppConfig as any, async (err, stats) => { if (err) { console.error(err.stack || err); - if (err.details) { - console.error(err.details); - } process.exit(1); } const info = stats.toJson(); @@ -1255,10 +1252,9 @@ export const buildApp = async () => { stats.toString({ chunks: false, assets: true, - entryPoints: false, + entrypoints: false, modules: false, moduleAssets: false, - moduleChunks: false, colors: true, }), ); @@ -1286,8 +1282,8 @@ export const buildApp = async () => { process.exit(); } // load the storage config - const storageConfig = require( - path.join(process.cwd(), 'scripts', 'storage-config'), + const storageConfig = await import( + path.join(process.cwd(), 'scripts', 'storage-config.js') ); // check if LincdFileStorage has a default FileStore diff --git a/src/cli.ts b/src/cli.ts index 1d50672..80b0717 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,6 +1,10 @@ -#!/usr/bin/env node -import babelRegister from '@babel/register'; -babelRegister({extensions: ['.ts', '.tsx']}); +#!/usr/bin/env tsx +//The line above calls the TSX typescript executer as runtime, which extends node.js and supports running typescript +// see: https://www.npmjs.com/package/tsx + +// import babelRegister from '@babel/register'; +// babelRegister({extensions: ['.ts', '.tsx']}); + import { addCapacitor, buildAll, @@ -24,13 +28,12 @@ import { publishUpdated, register, startServer, -} from './cli-methods'; +} from './cli-methods.js'; // import {buildMetadata} from './metadata'; -require('require-extensions'); - -var program = require('commander'); -var fs = require('fs-extra'); -var path = require('path'); +import 'require-extensions'; +import {program} from 'commander'; +import fs from 'fs-extra'; +import path from 'path'; program .command('create-app') @@ -72,6 +75,23 @@ program 'The base URL used for data of this package. Leave blank to use the URL of your package on lincd.org after you register it', ); +program + .command('fix-packages') + .action((name, uriBase) => { + return createPackage(name, uriBase); + }) + .description( + 'Create a new folder with all the required files for a new LINCD package', + ) + .argument( + '', + 'The name of the package. Will be used as package name in package.json', + ) + .argument( + '[uri_base]', + 'The base URL used for data of this package. Leave blank to use the URL of your package on lincd.org after you register it', + ); + program .command('create-shape') .action((name, uriBase) => { @@ -183,7 +203,7 @@ program.command('publish-updated').action(() => { return publishUpdated(); }); program.command('publish [version]').action((version) => { - return publishPackage(null, false, null, version); + publishPackage(null, false, null, version); }); program.command('status').action(() => { diff --git a/src/config-grunt.ts b/src/config-grunt.cts similarity index 97% rename from src/config-grunt.ts rename to src/config-grunt.cts index fd71eac..8a1ec89 100644 --- a/src/config-grunt.ts +++ b/src/config-grunt.cts @@ -1,10 +1,9 @@ import {generateWebpackConfig} from './config-webpack'; import {ModuleConfig} from './interfaces'; import {debug, flatten, generateScopedName, log, warn} from './utils'; - -const fs = require('fs'); -const chalk = require('chalk'); -const path = require('path'); +import fs from 'fs'; +import chalk from 'chalk'; +import path from 'path'; declare var __dirname: string; declare var require: any; @@ -254,7 +253,7 @@ function setupGrunt(grunt, moduleName, config: ModuleConfig) { dev: generateWebpackConfig( 'dev', moduleName, - (Object).assign( + (Object).assign( { target: 'es6', watch: true, @@ -267,7 +266,7 @@ function setupGrunt(grunt, moduleName, config: ModuleConfig) { 'dev-prod': generateWebpackConfig( 'dev', moduleName, - (Object).assign( + (Object).assign( { target: 'es6', watch: true, @@ -281,7 +280,7 @@ function setupGrunt(grunt, moduleName, config: ModuleConfig) { 'dev-es5': generateWebpackConfig( 'dev-es5', moduleName, - (Object).assign( + (Object).assign( { target: 'es5', watch: true, @@ -294,7 +293,7 @@ function setupGrunt(grunt, moduleName, config: ModuleConfig) { 'build-es6': generateWebpackConfig( 'build-es6', moduleName, - (Object).assign( + (Object).assign( { target: 'es6', watch: false, @@ -307,7 +306,7 @@ function setupGrunt(grunt, moduleName, config: ModuleConfig) { 'build-es5': generateWebpackConfig( 'build-es5', moduleName, - (Object).assign( + (Object).assign( { target: 'es5', watch: false, @@ -320,7 +319,7 @@ function setupGrunt(grunt, moduleName, config: ModuleConfig) { 'prod-es5': generateWebpackConfig( 'prod-es5', moduleName, - (Object).assign( + (Object).assign( { target: 'es5', watch: false, @@ -334,7 +333,7 @@ function setupGrunt(grunt, moduleName, config: ModuleConfig) { 'prod-es6': generateWebpackConfig( 'prod-es6', moduleName, - (Object).assign( + (Object).assign( { target: 'es6', watch: false, diff --git a/src/config-webpack-app.ts b/src/config-webpack-app.ts index 4f2630a..8b9612b 100644 --- a/src/config-webpack-app.ts +++ b/src/config-webpack-app.ts @@ -8,10 +8,10 @@ import fs from 'fs'; import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer'; import TerserPlugin from 'terser-webpack-plugin'; import {findNearestPackageJsonSync} from 'find-nearest-package-json'; -import {getLINCDDependencies, getLinkedTailwindColors} from './utils'; +import {getLINCDDependencies, getLinkedTailwindColors} from './utils.js'; -import tailwindPlugin from 'tailwindcss/plugin'; -import {LinkedFileStorage} from 'lincd/lib/utils/LinkedFileStorage'; +import tailwindPlugin from 'tailwindcss/plugin.js'; +import {LinkedFileStorage} from 'lincd/utils/LinkedFileStorage'; import postcssUrl from 'postcss-url'; const isProduction = process.env.NODE_ENV === 'production'; @@ -21,7 +21,7 @@ const packageJson = JSON.parse( ); // get from the project's backend config file -require(path.join(process.cwd(), 'scripts', 'storage-config')); +import(path.join(process.cwd(), 'scripts', 'storage-config.js')); const accessURL = LinkedFileStorage.accessURL; // Should relate to the use of express.static() in LincdServer.tsx, which makes the build files available through a URL @@ -31,33 +31,11 @@ const bundlesPath = publicPath + '/bundles/'; const ASSET_PATH = process.env.ASSET_PATH || accessURL ? accessURL + bundlesPath : bundlesPath; -const lincdConfigPath = path.resolve(process.cwd(), 'lincd.config.js'); +const lincdConfigPathJs = path.resolve(process.cwd(), 'lincd.config.js'); const lincdConfigPathJson = path.resolve(process.cwd(), 'lincd.config.json'); const cssModes = ['scss-modules', 'tailwind', 'scss', 'mixed']; -//default config -let config: any = { - //scss-modules is default - cssMode: cssModes[0], - analyse: false, -}; -//overwriting config from package.json.lincdApp or lincd.config.js(on) file -if (typeof packageJson.lincdApp === 'object') { - //overwrite default with anything that's defined in lincdApp in package.json - config = {...config, ...packageJson.lincdApp}; -} else if (fs.existsSync(lincdConfigPath)) { - config = {...config, ...require(lincdConfigPath)}; -} else if (fs.existsSync(lincdConfigPathJson)) { - config = {...config, ...require(lincdConfigPathJson)}; -} -if (!cssModes.includes(config.cssMode)) { - console.warn( - 'Invalid value for property cssMode. Should be one of: ' + - cssModes.join(', '), - ); - process.exit(); -} class RebuildScssJsonPlugin { apply(compiler) { @@ -119,6 +97,7 @@ class WatchRunPlugin { }); } } + function generateScopedName(name, filename, css) { var file = path.basename(filename, '.scss'); let nearestPackageJson = findNearestPackageJsonSync(filename); @@ -127,261 +106,288 @@ function generateScopedName(name, filename, css) { : packageJson.name; return packageName.replace(/[^a-zA-Z0-9_]+/g, '_') + '_' + file + '_' + name; } -// getLocalIdent(context,currentFormat,name) -// { -// var file = path.basename(context.resourcePath,'.scss'); -// return this.package.name.replace(/\-/g,"_") + '_' + file + '_' + name; -// } -let postcssPlugins = [ - postcssUrl({ - url: (asset) => { - return `${accessURL}${publicPath}${asset.url}`; - }, - }), -]; -if ( - config.cssMode === 'scss-modules' || - config.cssMode === 'scss' || - config.cssMode === 'mixed' -) { - postcssPlugins = postcssPlugins.concat([ - // ['stylelint', { - // 'extends': [ - // 'stylelint-config-standard' - // ], - // 'plugins': ['stylelint-scss'], - // 'rules': { - // 'at-rule-no-unknown': null, - // 'scss/at-rule-no-unknown': [ - // true, - // { - // ignoreAtRules: [ - // 'tailwind', - // 'apply', - // 'variants', - // 'responsive', - // 'screen', - // ], - // }, - // ], - // 'no-descending-specificity': null, - // 'at-rule-empty-line-before':null, - // 'rule-empty-line-before': null, - // 'no-missing-end-of-source-newline': null, - // 'max-line-length': null, - // 'color-function-notation': null, - // 'alpha-value-notation': null, - // 'number-max-precision':null, - // }, - // }], - 'postcss-preset-env', - isProduction && 'cssnano', - // "postcss-reporter", - ]); - if (config.cssMode === 'scss-modules' || config.cssMode === 'mixed') { - postcssPlugins.push([ - 'postcss-modules', - { - generateScopedName: generateScopedName, - globalModulePaths: (Array.isArray(config.cssGlobalModulePaths) - ? [/tailwind/, ...config.cssGlobalModulePaths] - : [/tailwind/, config.cssGlobalModulePaths] - ).filter(Boolean), +export const getLincdConfig = async () => { + //default config + let config: any = { + //scss-modules is default + cssMode: cssModes[0], + analyse: false, + }; +//overwriting config from package.json.lincdApp or lincd.config.js(on) file + if (typeof packageJson.lincdApp === 'object') { + //overwrite default with anything that's defined in lincdApp in package.json + config = {...config, ...packageJson.lincdApp}; + } else if (fs.existsSync(lincdConfigPathJs)) { + let lincdConfig = await import(lincdConfigPathJs); + config = {...config, ...lincdConfig}; + } else if (fs.existsSync(lincdConfigPathJson)) { + let lincdConfig = JSON.parse(fs.readFileSync(lincdConfigPathJson, 'utf-8')) + config = {...config, ...lincdConfig}; + } + return config; +} + +export const getWebpackAppConfig = async () => { + + let config = await getLincdConfig(); + + if (!cssModes.includes(config.cssMode)) { + console.warn( + 'Invalid value for property cssMode. Should be one of: ' + + cssModes.join(', '), + ); + process.exit(); + } + let postcssPlugins = [ + postcssUrl({ + url: (asset) => { + return `${accessURL}${publicPath}${asset.url}`; }, + }), + ]; + if ( + config.cssMode === 'scss-modules' || + config.cssMode === 'scss' || + config.cssMode === 'mixed' + ) { + postcssPlugins = postcssPlugins.concat([ + // ['stylelint', { + // 'extends': [ + // 'stylelint-config-standard' + // ], + // 'plugins': ['stylelint-scss'], + // 'rules': { + // 'at-rule-no-unknown': null, + // 'scss/at-rule-no-unknown': [ + // true, + // { + // ignoreAtRules: [ + // 'tailwind', + // 'apply', + // 'variants', + // 'responsive', + // 'screen', + // ], + // }, + // ], + // 'no-descending-specificity': null, + // 'at-rule-empty-line-before':null, + // 'rule-empty-line-before': null, + // 'no-missing-end-of-source-newline': null, + // 'max-line-length': null, + // 'color-function-notation': null, + // 'alpha-value-notation': null, + // 'number-max-precision':null, + // }, + // }], + 'postcss-preset-env', + isProduction && 'cssnano', + // "postcss-reporter", ]); + if (config.cssMode === 'scss-modules' || config.cssMode === 'mixed') { + postcssPlugins.push([ + 'postcss-modules', + { + generateScopedName: generateScopedName, + globalModulePaths: (Array.isArray(config.cssGlobalModulePaths) + ? [/tailwind/, ...config.cssGlobalModulePaths] + : [/tailwind/, config.cssGlobalModulePaths] + ).filter(Boolean), + }, + ]); + } } -} -if (config.cssMode === 'tailwind' || config.cssMode === 'mixed') { - //make sure that tailwind classes from any LINCD packages that are listed in package.json:dependencies are included - let lincdPackagePaths: any = getLINCDDependencies(packageJson); - lincdPackagePaths = lincdPackagePaths.map(([packageName, packagePath]) => { - return packagePath + '/lib/**/*.{js,mjs}'; - }); - // console.log( - // chalk.blueBright('tailwind content: ') + chalk.magenta(['./frontend/src/**/*.{tsx,ts}', ...lincdPackagePaths]), - // ); - postcssPlugins.push([ - 'tailwindcss', - { - content: [ - (process.env.SOURCE_PATH || './src/') + '**/*.{tsx,ts}', - ...lincdPackagePaths, - ], - safelist: isProduction - ? {} - : { + if (config.cssMode === 'tailwind' || config.cssMode === 'mixed') { + //make sure that tailwind classes from any LINCD packages that are listed in package.json:dependencies are included + let lincdPackagePaths: any = getLINCDDependencies(packageJson); + lincdPackagePaths = lincdPackagePaths.map(([packageName, packagePath]) => { + return packagePath + '/lib/**/*.{js,mjs}'; + }); + // console.log( + // chalk.blueBright('tailwind content: ') + chalk.magenta(['./frontend/src/**/*.{tsx,ts}', ...lincdPackagePaths]), + // ); + postcssPlugins.push([ + 'tailwindcss', + { + content: [ + (process.env.SOURCE_PATH || './src/') + '**/*.{tsx,ts}', + ...lincdPackagePaths, + ], + safelist: isProduction + ? {} + : { //in development mode we allow all classes here, so that you can easily develop pattern: /./, variants: ['sm', 'md', 'lg', 'xl', '2xl'], }, - theme: { - extend: { - colors: getLinkedTailwindColors(), + theme: { + extend: { + colors: getLinkedTailwindColors(), + }, }, + plugins: [ + tailwindPlugin(function ({addBase, config}) { + //we can use LINCD CSS variables for default font color, size etc. + // addBase({ + // 'h1': { fontSize: config('theme.fontSize.2xl') }, + // 'h2': { fontSize: config('theme.fontSize.xl') }, + // 'h3': { fontSize: config('theme.fontSize.lg') }, + // }) + }), + ], }, - plugins: [ - tailwindPlugin(function ({addBase, config}) { - //we can use LINCD CSS variables for default font color, size etc. - // addBase({ - // 'h1': { fontSize: config('theme.fontSize.2xl') }, - // 'h2': { fontSize: config('theme.fontSize.xl') }, - // 'h3': { fontSize: config('theme.fontSize.lg') }, - // }) - }), - ], - }, - ]); -} + ]); + } -// console.log("OUTPUT IN ",path.resolve(process.cwd(), './frontend/build')); -export const webpackAppConfig = { - mode: isProduction ? 'production' : 'development', - devtool: isProduction ? 'source-map' : 'cheap-module-source-map', - entry: [ - isDevelopment && 'webpack-hot-middleware/client', - path.resolve( - process.cwd(), - process.env.ENTRY_PATH || + return { + mode: isProduction ? 'production' : 'development', + devtool: isProduction ? 'source-map' : 'cheap-module-source-map', + entry: [ + isDevelopment && 'webpack-hot-middleware/client', + path.resolve( + process.cwd(), + process.env.ENTRY_PATH || (process.env.SOURCE_PATH ? process.env.SOURCE_PATH + '/index.tsx' : './src/index.tsx'), - ), - ].filter(Boolean), - watch: isDevelopment || config.analyse, - output: { - path: path.resolve( - process.cwd(), - process.env.OUTPUT_PATH || './public/bundles', - ), - filename: '[name].bundle.js', - publicPath: ASSET_PATH, - clean: true, - }, - watchOptions: { - ignored: ['**/*.d.ts', '**/*.js.map', '**/*.scss.json'], - aggregateTimeout: 500, - }, - devServer: { - client: { - progress: false, + ), + ].filter(Boolean), + watch: isDevelopment || config.analyse, + output: { + path: path.resolve( + process.cwd(), + process.env.OUTPUT_PATH || './public/bundles', + ), + filename: '[name].bundle.js', + publicPath: ASSET_PATH, + clean: true, }, - }, - plugins: [ - // new WatchRunPlugin(), - new RebuildScssJsonPlugin(), - new MiniCssExtractPlugin(), - new webpack.EnvironmentPlugin(Object.keys(process.env)), - // new ForkTsCheckerWebpackPlugin(), - isDevelopment && new ReactRefreshWebpackPlugin(), - isDevelopment && new webpack.HotModuleReplacementPlugin(), - config.analyse && new BundleAnalyzerPlugin(), - ...(Array.isArray(config.plugins) ? config.plugins : []), - ].filter(Boolean), - externals: config.externals || {}, - module: { - rules: [ - { - test: /\.(scss|css)$/, - use: [ - MiniCssExtractPlugin.loader, - { - loader: 'css-loader', - options: { - url: false, - // modules:{ - // exportOnlyLocals:true, - // getLocalIdent: this.getLocalIdent.bind(this), - // localIdentName: "[path][name]__[local]--[hash:base64:5]", - // }, - importLoaders: 1, - }, - }, - { - loader: 'postcss-loader', - options: { - postcssOptions: { - plugins: postcssPlugins, + watchOptions: { + ignored: ['**/*.d.ts','**/*.js.map','**/*.scss.json'], + aggregateTimeout: 500, + }, + devServer: { + client: { + progress: false, + }, + }, + plugins: [ + // new WatchRunPlugin(), + new RebuildScssJsonPlugin(), + new MiniCssExtractPlugin(), + new webpack.EnvironmentPlugin(Object.keys(process.env)), + // new ForkTsCheckerWebpackPlugin(), + isDevelopment && new ReactRefreshWebpackPlugin(), + isDevelopment && new webpack.HotModuleReplacementPlugin(), + config.analyse && new BundleAnalyzerPlugin(), + ...(Array.isArray(config.plugins) ? config.plugins : []), + ].filter(Boolean), + externals: config.externals || {}, + module: { + rules: [ + { + test: /\.(scss|css)$/, + use: [ + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + url: false, + // modules:{ + // exportOnlyLocals:true, + // getLocalIdent: this.getLocalIdent.bind(this), + // localIdentName: "[path][name]__[local]--[hash:base64:5]", + // }, + importLoaders: 1, }, }, - }, - { - loader: 'sass-loader', - options: {sourceMap: true}, - }, - ], - }, - // { - // test: /\.(ts|tsx)$/, - // exclude: /node_modules/, - // include: [path.join(process.cwd(),"frontend")], // only bundle files in this directory - // use: { - // loader: "babel-loader", // cf. .babelrc.json in this folder and browser list in package.json - // options: { - // plugins: isDevelopment ? ["react-refresh/babel"] : [], - // cacheCompression: false, - // cacheDirectory: true, - // }, - // }, - // }, - // { - // test: /\.m?js/, - // resolve: { - // fullySpecified: false - // } - // }, - { - test: /\.tsx?$/, - use: [ - { - loader: - 'ts-loader?' + - JSON.stringify({ - compilerOptions: { - //this is required for dynamic imports & code splitting - module: 'esnext', - moduleResolution: 'node', - sourceMap: isDevelopment, + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: postcssPlugins, }, - }), - options: { - ...(isDevelopment - ? { + }, + }, + { + loader: 'sass-loader', + options: { sourceMap: true }, + }, + ], + }, + // { + // test: /\.(ts|tsx)$/, + // exclude: /node_modules/, + // include: [path.join(process.cwd(),"frontend")], // only bundle files in this directory + // use: { + // loader: "babel-loader", // cf. .babelrc.json in this folder and browser list in package.json + // options: { + // plugins: isDevelopment ? ["react-refresh/babel"] : [], + // cacheCompression: false, + // cacheDirectory: true, + // }, + // }, + // }, + // { + // test: /\.m?js/, + // resolve: { + // fullySpecified: false + // } + // }, + { + test: /\.tsx?$/, + use: [ + { + loader: + 'ts-loader?' + + JSON.stringify({ + compilerOptions: { + //this is required for dynamic imports & code splitting + module: 'esnext', + moduleResolution: 'node', + sourceMap: isDevelopment, + }, + }), + options: { + ...(isDevelopment + ? { getCustomTransformers: () => ({ before: [ReactRefreshTypeScript()], }), } - : {}), - transpileOnly: isProduction, + : {}), + transpileOnly: isProduction, + }, }, - }, - ], - exclude: /node_modules/, - }, - ], - }, - optimization: { - minimize: isProduction, - minimizer: [ - new TerserPlugin({ - terserOptions: { - keep_classnames: true, + ], + exclude: /node_modules/, }, - }), - ], - }, - stats: { - chunks: false, - assets: true, - entrypoints: false, - children: true, - modules: false, - }, - resolve: { - extensions: ['.tsx', '.ts', '.js', '.scss', '.scss.json', '.json'], - alias: config.alias || {}, - }, - //Cache is now overwritten in LincdServer based on config, the other value for type would be 'filesystem' - //see also https://webpack.js.org/configuration/other-options/#cache - cache: {type: 'memory'}, -}; + ], + }, + optimization: { + minimize: isProduction, + minimizer: [ + new TerserPlugin({ + terserOptions: { + keep_classnames: true, + }, + }), + ], + }, + stats: { + chunks: false, + assets: true, + entrypoints: false, + children: true, + modules: false, + }, + resolve: { + extensions: ['.tsx','.ts','.js','.scss','.scss.json','.json'], + alias: config.alias || {}, + }, + //Cache is now overwritten in LincdServer based on config, the other value for type would be 'filesystem' + //see also https://webpack.js.org/configuration/other-options/#cache + cache: { type: 'memory' }, + } +} \ No newline at end of file diff --git a/src/config-webpack.ts b/src/config-webpack.ts index d03a329..7482d15 100644 --- a/src/config-webpack.ts +++ b/src/config-webpack.ts @@ -8,23 +8,21 @@ import { warn, } from './utils'; import {AdjustedModuleConfig} from './interfaces'; -import colors = require('colors'); +import * as colors from 'colors'; // console.log('Webpack '+require('webpack/package.json').version); // console.log('ts-loader '+require('ts-loader/package.json').version); -const fs = require('fs'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const chalk = require('chalk'); -const webpack = require('webpack'); -const path = require('path'); +import fs from 'fs'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import webpack from 'webpack'; +import path from 'path'; // const WebpackLicencePlugin = require('webpack-license-plugin'); // const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); -const BundleAnalyzerPlugin = - require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -const TerserPlugin = require('terser-webpack-plugin'); -const exec = require('child_process').exec; -const CopyPlugin = require('copy-webpack-plugin'); -const tailwindPlugin = require('tailwindcss/plugin'); +import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; +import TerserPlugin from 'terser-webpack-plugin'; +import { exec } from 'child_process'; +import CopyPlugin from 'copy-webpack-plugin'; +import tailwindPlugin from 'tailwindcss/plugin'; declare var __dirname: string; declare var require: any; @@ -85,9 +83,9 @@ export function generateWebpackConfig( process.exit(); } - let tsConfig = JSON.parse(fs.readFileSync(configFile)); + let tsConfig = JSON.parse(fs.readFileSync(configFile,'utf8')); - var plugins = [ + var plugins:any[] = [ // new webpack.DefinePlugin({ // 'process.env.BROWSER': JSON.stringify(true), // 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), @@ -354,7 +352,7 @@ export function generateWebpackConfig( plugins.push( new webpack.NormalModuleReplacementPlugin( /lincd\/lib\//, - (resource, match) => { + (resource) => { let moduleName = resource.request.match(/lincd\/lib\//)[1]; if (config.internalsources.indexOf(moduleName) !== -1) { console.log( diff --git a/src/index.ts b/src/index.ts index 6af2e29..1e91b88 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ export {default as DeclarationPlugin} from './plugins/declaration-plugin'; export {default as externaliseModules} from './plugins/externalise-modules'; export {default as checkImports} from './plugins/check-imports'; -export {default as generateGruntConfig} from './config-grunt'; +// export {default as generateGruntConfig} from './config-grunt'; // export {buildMetadata} from './metadata'; import {generateWebpackConfig} from './config-webpack'; diff --git a/src/plugins/declaration-plugin.ts b/src/plugins/declaration-plugin.ts index 3ffbf86..9a34f40 100644 --- a/src/plugins/declaration-plugin.ts +++ b/src/plugins/declaration-plugin.ts @@ -1,8 +1,10 @@ /// -import colors = require('colors'); -import path = require('path'); +import colors from 'colors'; +import * as path from 'path'; -const webpack = require('webpack'); +import webpack from 'webpack'; +import * as process from 'process'; +import fs from 'fs'; const {Compilation} = webpack; export default class DeclarationPlugin { options: any; @@ -26,7 +28,9 @@ export default class DeclarationPlugin { this.options['root'] = options.root || this.exportRoot; //'/lib' this.logMessages = options.debug ? options.debug : false; - this.modulePackageInfo = require(process.cwd() + '/package.json'); + //load package.json with fs + this.modulePackageInfo = JSON.parse(fs.readFileSync(process.cwd() + '/package.json', 'utf8')); + // this.modulePackageInfo = await import(process.cwd() + '/package.json'); // this.debug('found package name: '+this.modulePackageInfo.name); } diff --git a/src/plugins/externalise-modules.ts b/src/plugins/externalise-modules.ts index fd9fb5a..3727175 100644 --- a/src/plugins/externalise-modules.ts +++ b/src/plugins/externalise-modules.ts @@ -1,5 +1,6 @@ /// -import colors = require('colors'); +import colors from 'colors'; +import fs from 'fs'; var exportRoot = '/lib'; var libraryName = 'lincd'; @@ -199,7 +200,7 @@ function isLincdModule(debug, packageName: string) { let isLincdModule: boolean; let modulePackage; try { - modulePackage = require(packageName + '/package.json'); + modulePackage = JSON.parse(fs.readFileSync(packageName + '/package.json','utf8')); } catch (e) { debug(colors.red(packageName + '/package.json' + ' does not exist')); // return callback(); diff --git a/src/utils.ts b/src/utils.ts index 1a0e5ee..afd2fd5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,11 +6,9 @@ import ts from 'typescript'; import {builtinModules} from 'module'; import {PackageDetails} from 'interfaces'; -const { - findNearestPackageJson, - findNearestPackageJsonSync, -} = require('find-nearest-package-json'); -var glob = require('glob'); +import { findNearestPackageJson,findNearestPackageJsonSync } from 'find-nearest-package-json'; +import * as glob from 'glob'; + var gruntConfig; // Credit: https://gist.github.com/tinovyatkin/727ddbf7e7e10831a1eca9e4ff2fc32e diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json new file mode 100644 index 0000000..d55ce6f --- /dev/null +++ b/tsconfig-cjs.json @@ -0,0 +1,15 @@ +{ + "extends" : "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "target": "es2018", + "outDir": "lib/cjs", + "sourceMap": true, + "skipLibCheck": true, + "downlevelIteration": true, + "baseUrl": "./src", + "types": ["node"] + }, + "include": ["./src/**/*.ts","./src/**/*.cts"], + "files": ["./src/index.ts", "./src/cli.ts"] +} diff --git a/tsconfig-esm.json b/tsconfig-esm.json new file mode 100644 index 0000000..e2e1e44 --- /dev/null +++ b/tsconfig-esm.json @@ -0,0 +1,16 @@ +{ + "extends" : "./tsconfig.json", + "compilerOptions": { + "module": "esnext", + "target": "es6", + "outDir": "lib/esm", + "moduleResolution": "node", + "sourceMap": true, + "skipLibCheck": true, + "downlevelIteration": true, + "baseUrl": "./src", + "types": ["node"] + }, + "include": ["./src/**/*.ts"], + "files": ["./src/index.ts", "./src/cli.ts"] +} diff --git a/tsconfig.json b/tsconfig.json index cb675fd..71e0156 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "target": "es2018", "baseUrl": "./src", "sourceMap": true, + "skipLibCheck": true, "downlevelIteration": true, "outDir": "./lib", "paths": { From e84ac7b3311aa18d1791cc04ca3d026a378d3c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Mon, 22 Apr 2024 17:54:26 +0800 Subject: [PATCH 06/87] remake of `lincd build` command, without grunt! lincd CLI is itself now a dual package. --- package.json | 9 +- src/cli-methods.ts | 276 +++++++++++++++++++++++++++++++++++---------- src/cli.ts | 35 +++--- tsconfig-cjs.json | 10 +- tsconfig-esm.json | 10 +- tsconfig.json | 16 +-- 6 files changed, 246 insertions(+), 110 deletions(-) diff --git a/package.json b/package.json index 869b1ad..967e0f0 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,9 @@ }, "scripts": { "prepublishOnly": "npm exec build", - "build": "rimraf ./lib && echo \"compiling CJS\" && yarn tsc -p tsconfig-cjs.json && echo \"compiling ESM\" && yarn tsc -p tsconfig-esm.json && yarn dual-package", + "build": "rimraf ./lib && yarn build-esm && yarn dual-package", + "build-cjs": "echo \"compiling CJS\" && yarn tsc -p tsconfig-cjs.json", + "build-esm": "echo \"compiling ESM\" && yarn tsc -p tsconfig-esm.json", "dual-package": "echo 'adding package.json to each lib folder for dual output support' && yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json", "dev": "yarn tsc -p tsconfig-esm.json -w", "dev-cjs": "yarn tsc -p tsconfig-cjs.json -w", @@ -73,7 +75,7 @@ "@babel/register": "^7.23.7", "@lodder/grunt-postcss": "^3.1.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", - "@types/node": "^18.11.10", + "@types/node": "^20.12.7", "@types/react": "^18.0.17", "@types/react-dom": "^18.0.6", "babel-loader": "^9.1.3", @@ -82,6 +84,7 @@ "colors": "^1.4.0", "commander": "^11.0.0", "copy-webpack-plugin": "^12.0.2", + "copyfiles": "^2.4.1", "css-loader": "^6.10.0", "depcheck": "^1.4.7", "env-cmd": "^10.1.0", @@ -103,6 +106,7 @@ "load-grunt-tasks": "^5.1.0", "mini-css-extract-plugin": "^2.8.1", "open": "^8.4.0", + "ora": "^8.0.1", "postcss": "^8.4.35", "postcss-comment": "^2.0.0", "postcss-import": "^16.0.1", @@ -118,6 +122,7 @@ "source-map-loader": "^5.0.0", "staged-git-files": "^1.3.0", "tailwindcss": "^3.4.1", + "terminal-kit": "^3.1.1", "terser-webpack-plugin": "^5.3.10", "ts-loader": "^9.5.1", "tsconfig-paths-webpack-plugin": "^4.1.0", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 01c3a87..7bd1b06 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -3,6 +3,7 @@ import {exec} from 'child_process'; import depcheck from 'depcheck'; import {getEnvFile} from 'env-cmd/dist/get-env-vars.js'; import fs from 'fs-extra'; +import ts from 'typescript'; import path from 'path'; import { debugInfo, @@ -28,6 +29,8 @@ import { glob } from 'glob'; import webpack from 'webpack'; import stagedGitFiles from 'staged-git-files'; +import copyfiles from 'copyfiles'; +import ora from 'ora'; var variables = {}; export const createApp = async (name, basePath = process.cwd()) => { @@ -1058,30 +1061,36 @@ export const checkImports = async ( }); } + let res = ''; // All recursion must have finished, display any errors if (depth === 0 && invalidImports.size > 0) { - console.warn(chalk.red('\n' + 'Invalid imports found. See fixes below:')); - console.warn( - chalk.red( - " - For relative imports, ensure you don't import outside of the /src/ folder", - ), - ); - console.warn( - chalk.red( - ' - For lincd imports, access the /lib/ folder instead of /src/', - ), - ); + + res += chalk.red('Invalid imports found.\n'); invalidImports.forEach((value, key) => { - console.info( - chalk.red('\nFound in file ') + chalk.blue(key) + chalk.red(':'), - ); - value.forEach((i) => console.warn(chalk.red("- '" + i + "'"))); + // res += '- '+chalk.blueBright(key.split('/').pop()) + ':\n'; + value.forEach((i) => { + res += chalk.red(key.split('/').pop()+" imports from '" + i + "'\n"); + if(i.indexOf('../../') === 0) + { + res += + "To fix: import from the NPM package directly.\n"; + } + else if('/src/') { + res += "To fix: you likely need to replace /src with /lib\n"; + } + + }); }); - process.exit(1); + + + + throw res; + // process.exit(1); } else if (depth === 0 && invalidImports.size === 0) { - console.info('All imports OK'); - process.exit(0); + // console.info('All imports OK'); + // process.exit(0); + return true; } }; @@ -1109,51 +1118,54 @@ export const depCheckStaged = async () => { }); }); }; -export const depCheck = async (path: string = process.cwd()) => { - depcheck(path, {}, (results) => { - if (results.missing) { - let lincdPackages = getLocalLincdModules(); - let missing = Object.keys(results.missing); - //filter out missing types, if it builds we're not too concerned about that at the moment? - //especially things like @types/react, @types/react-dom, @types/node (they are added elsewhere?) - // missing = missing.filter(m => m.indexOf('@types/') === 0); - //currently react is not an explicit dependency, but we should add it as a peer dependency - missing.splice(missing.indexOf('react'), 1); - - let missingLincdPackages = missing.filter((missingDep) => { - return lincdPackages.some((lincdPackage) => { - return lincdPackage.packageName === missingDep; +export const depCheck = async (packagePath: string = process.cwd()) => { + return new Promise((resolve,reject) => { + depcheck(packagePath,{},(results) => { + if (results.missing) + { + let lincdPackages = getLocalLincdModules(); + let missing = Object.keys(results.missing); + //filter out missing types, if it builds we're not too concerned about that at the moment? + //especially things like @types/react, @types/react-dom, @types/node (they are added elsewhere?) + // missing = missing.filter(m => m.indexOf('@types/') === 0); + //currently react is not an explicit dependency, but we should add it as a peer dependency + missing.splice(missing.indexOf('react'),1); + + let missingLincdPackages = missing.filter((missingDep) => { + return lincdPackages.some((lincdPackage) => { + return lincdPackage.packageName === missingDep; + }); }); - }); - //currently just missing LINCD packages cause a hard failure exit code - if (missingLincdPackages.length > 0) { - console.warn( - chalk.red( - path + - '\n[ERROR] These LINCD packages are imported but they are not listed in package.json:\n- ' + - missingLincdPackages.join(',\n- '), - ), - ); - process.exit(1); - } else if (missing.length > 0) { - console.warn( - chalk.magenta( - path + - '\nMissing dependencies (for now a warning, soon an error):\n\t' + - missing.join(',\n\t'), - ), - ); + //currently just missing LINCD packages cause a hard failure exit code + if (missingLincdPackages.length > 0) + { + reject(chalk.red( + packagePath.split("/").pop() + + '\n[ERROR] These LINCD packages are imported but they are not listed in package.json:\n- ' + + missingLincdPackages.join(',\n- '), + )); + } + else if (missing.length > 0) + { + resolve(chalk.redBright( + 'warning: '+packagePath.split("/").pop() + + ' is missing dependencies:\n - ' + + missing.join('\n - '), + )); + } else { + resolve(true); + } } - } - // if(Object.keys(results.invalidFiles).length > 0) { - // console.warn(chalk.red("Invalid files:\n")+Object.keys(results.invalidFiles).join(",\n")); - // } - // if(Object.keys(results.invalidDirs).length > 0) { - // console.warn(chalk.red("Invalid dirs:\n")+results.invalidDirs.toString()); - // } - // if(results.unused) { - // console.warn("Unused dependencies: "+results.missing.join(", ")); - // } + // if(Object.keys(results.invalidFiles).length > 0) { + // console.warn(chalk.red("Invalid files:\n")+Object.keys(results.invalidFiles).join(",\n")); + // } + // if(Object.keys(results.invalidDirs).length > 0) { + // console.warn(chalk.red("Invalid dirs:\n")+results.invalidDirs.toString()); + // } + // if(results.unused) { + // console.warn("Unused dependencies: "+results.missing.join(", ")); + // } + }); }); }; export const ensureEnvironmentLoaded = async () => { @@ -1320,6 +1332,23 @@ export const buildApp = async () => { }); }; +export const upgradePackages = async () => { + await ensureEnvironmentLoaded(); + let packages = getLincdPackages(); + packages = packages.filter((pkg) => { + pkg.packageName !== 'lincd' + }); + packages.forEach(pkg => { + console.log('Upgrading ' + pkg.packageName); + // execPromise(`cd ${pkg.path} && yarn upgrade`).then(() => { + // console.log('Upgraded ' + pkg.packageName); + // }).catch(err => { + // console.warn(err); + // }) + }) + +} + export const createPackage = async ( name, uriBase?, @@ -1543,12 +1572,135 @@ export const register = function (registryURL) { } }; -export const buildPackage = ( +export const buildPackage = async ( target, target2, packagePath = process.cwd(), logResults: boolean = true, ) => { + + const spinner = ora({ + discardStdin: true, + text: 'Compiling code', + }).start(); + let buildProcess:Promise = Promise.resolve(true); + let buildStep = (step) => { + buildProcess = buildProcess.then((previousResult) => { + spinner.text = step.name; + spinner.start(); + return step.apply().then(stepResult => { + if(typeof stepResult === 'string') { + // spinner.text = step.name + ' - ' + stepResult; + spinner.warn(step.name + ' - ' + stepResult) + spinner.stop(); + //warning is shown, but build is still succesful with warnings + return false; + } else if(stepResult === true || typeof stepResult === 'undefined') { + spinner.succeed(); + return previousResult && true; + } + }) + }); + } + + buildStep({ + name: 'Compiling code', + apply: async () => { + //echo 'compiling CJS' && tsc -p tsconfig-cjs.json && echo 'compiling ESM' && tsc -p tsconfig-esm.json + let cjsConfig = fs.existsSync(path.join(packagePath,'tsconfig-cjs.json')); + let esmConfig = fs.existsSync(path.join(packagePath,'tsconfig-esm.json')); + let compileCJS = `yarn exec tsc -p tsconfig-cjs.json`; + let compileESM = `yarn exec tsc -p tsconfig-esm.json`; + let compileCommand; + if(cjsConfig && esmConfig) { + compileCommand = `${compileCJS} && ${compileESM}`; + } else if(cjsConfig) { + compileCommand = compileCJS; + } else if(esmConfig) { + compileCommand = compileESM; + } else { + compileCommand = `yarn exec tsc`; + } + return execPromise(compileCommand).then(res => { + return res === ''; + }) + } + }); + buildStep({ + name: 'Copying files', + apply: async () => { + return Promise.all( + [new Promise((resolve,reject) => { + copyfiles(['src/**/*.json', 'src/**/*.d.ts', 'src/**/*.scss', 'src/**/*.css','lib/esm'],1,(err) => { + if(err) { + reject(err); + } + else + { + resolve(true); + } + }) + }), + new Promise((resolve,reject) => { + copyfiles(['src/**/*.json','src/**/*.d.ts','src/**/*.scss','src/**/*.css','lib/cjs'],1,(err) => { + if (err) + { + return reject(err); + } + resolve(true); + }) + }) + ]).then((results) => { + return results.every(r => r === true) + }); + } + }); + buildStep({ + name: 'Checking dependencies', + apply: depCheck + }); + buildStep({ + name: 'Checking imports', + apply: checkImports + }); + + let success = await buildProcess.catch(err => { + let msg = (err && err.stdout && err.error) ? (chalk.red('error')+err.error + ':\n'+err.stdout) : err.toString(); + spinner.stopAndPersist({ + symbol: chalk.red('✖'), + text: 'Build failed', + }); + console.log(msg); + }); + //will be undefined if there was an error + if(typeof success !== 'undefined') + { + spinner.stopAndPersist({ + symbol: chalk.greenBright('✔'), + text: success === true ? 'Build successful' : 'Build successful with warnings', + }); + } + return success; + // 'build-lib': 'yarn exec tsc --pretty', + + // 'copy:lib', + // var copyfiles = require('copyfiles'); + // copyfiles([paths], opt, callback); + + // { + // expand: true, + // src: ['**/*.json', '**/*.d.ts', '**/*.scss', '**/*.css'], + // dest: config.outputPath || 'lib/', + // cwd: 'src/', + // filter: 'isFile', + // }, + + + + // command: 'yarn lincd depcheck', + //'check-imports': 'yarn lincd check-imports', + + if (target == 'production' || target == 'es5' || target == 'es6' || !target) { if (!fs.existsSync(path.join(packagePath, 'Gruntfile.js'))) { console.warn( diff --git a/src/cli.ts b/src/cli.ts index 80b0717..8517ac2 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -27,13 +27,13 @@ import { publishPackage, publishUpdated, register, - startServer, + startServer,upgradePackages, } from './cli-methods.js'; // import {buildMetadata} from './metadata'; import 'require-extensions'; import {program} from 'commander'; import fs from 'fs-extra'; -import path from 'path'; +import path, { dirname } from 'path'; program .command('create-app') @@ -76,20 +76,12 @@ program ); program - .command('fix-packages') - .action((name, uriBase) => { - return createPackage(name, uriBase); + .command('upgrade-packages') + .action(() => { + return upgradePackages(); }) .description( - 'Create a new folder with all the required files for a new LINCD package', - ) - .argument( - '', - 'The name of the package. Will be used as package name in package.json', - ) - .argument( - '[uri_base]', - 'The base URL used for data of this package. Leave blank to use the URL of your package on lincd.org after you register it', + 'Upgrade all lincd packages in the workspace to ESM/CJS dual packages', ); program @@ -169,11 +161,18 @@ program program .command('info') .action(() => { - var ownPackage = JSON.parse( - fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'), - ); + const localDir = dirname(import.meta.url).replace('file:/', ''); + let packageJsonPath =path.join(localDir, 'package.json') + try { + var ownPackage = JSON.parse( + fs.readFileSync(packageJsonPath, 'utf8'), + ); + } catch (e) { + console.warn('Could not read package.json at '+packageJsonPath+': '+e); + process.exit(); + } console.log(ownPackage.version); - console.log('Running from: ' + __dirname); + console.log('Running from: ' + localDir); }) .description( "Log the version of this tool and the path that it's running from", diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json index d55ce6f..ef98218 100644 --- a/tsconfig-cjs.json +++ b/tsconfig-cjs.json @@ -1,15 +1,9 @@ { "extends" : "./tsconfig.json", "compilerOptions": { + "esModuleInterop": true, "module": "commonjs", "target": "es2018", "outDir": "lib/cjs", - "sourceMap": true, - "skipLibCheck": true, - "downlevelIteration": true, - "baseUrl": "./src", - "types": ["node"] - }, - "include": ["./src/**/*.ts","./src/**/*.cts"], - "files": ["./src/index.ts", "./src/cli.ts"] + } } diff --git a/tsconfig-esm.json b/tsconfig-esm.json index e2e1e44..813e2f5 100644 --- a/tsconfig-esm.json +++ b/tsconfig-esm.json @@ -5,12 +5,6 @@ "target": "es6", "outDir": "lib/esm", "moduleResolution": "node", - "sourceMap": true, - "skipLibCheck": true, - "downlevelIteration": true, - "baseUrl": "./src", - "types": ["node"] - }, - "include": ["./src/**/*.ts"], - "files": ["./src/index.ts", "./src/cli.ts"] + "allowSyntheticDefaultImports": true, + } } diff --git a/tsconfig.json b/tsconfig.json index 71e0156..28a67cb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,11 @@ { "compilerOptions": { - "module": "commonjs", - "newLine": "lf", - "esModuleInterop": true, - "target": "es2018", - "baseUrl": "./src", "sourceMap": true, "skipLibCheck": true, - "downlevelIteration": true, - "outDir": "./lib", - "paths": { - "*": ["node_modules/@types/*", "../node_modules/@types/*", "../../node_modules/@types/*", "*"] - }, + "baseUrl": "./src", + "module": "esnext", "types": ["node"] }, - "include": ["./src/**/*.ts"], + "include": ["./src/**/*.ts","./src/**/*.cts"], "files": ["./src/index.ts", "./src/cli.ts"] -} +} \ No newline at end of file From a4c1eedec2383937fc83e132b9fb8dce209a1e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Sat, 11 May 2024 18:37:06 +0200 Subject: [PATCH 07/87] added custom css loader for node.js, currently needs to be used like this: `node --import ./node_modules/lincd-cli/lib/esm/loaders/register.js app.js` adjusted default package setup, not using registerPackageModule anymore added types to default package setup updated default ontology import default dual package setup --- defaults/app-with-backend/package.json | 10 +- defaults/app-with-backend/src/package.ts | 1 - defaults/component.tsx | 4 +- defaults/package/package.json | 2 +- defaults/package/src/index.ts | 1 + .../src/ontologies/example-ontology.ts | 9 +- defaults/package/src/package.ts | 1 - defaults/package/src/types.d.ts | 9 + defaults/package/tsconfig-cjs.json | 8 + defaults/package/tsconfig-es5.json | 20 - defaults/package/tsconfig-esm.json | 9 + defaults/package/tsconfig.json | 17 +- defaults/set-component.tsx | 7 +- package.json | 14 +- src/cli-methods.ts | 1539 +++++++++++------ src/cli.ts | 4 +- src/loaders/css-loader.mts | 75 + src/loaders/register.ts | 3 + src/metadata.ts | 308 ++-- src/utils.ts | 20 +- tsconfig.json | 2 +- 21 files changed, 1278 insertions(+), 785 deletions(-) create mode 100644 defaults/package/src/types.d.ts create mode 100644 defaults/package/tsconfig-cjs.json delete mode 100644 defaults/package/tsconfig-es5.json create mode 100644 defaults/package/tsconfig-esm.json create mode 100644 src/loaders/css-loader.mts create mode 100644 src/loaders/register.ts diff --git a/defaults/app-with-backend/package.json b/defaults/app-with-backend/package.json index 0577c79..c094fd6 100644 --- a/defaults/app-with-backend/package.json +++ b/defaults/app-with-backend/package.json @@ -59,10 +59,10 @@ "@babel/register": "^7.22.5", "chalk": "^4.1.2", "lincd": "^1.0", - "lincd-auth": "^0.1.19", - "lincd-jsonld": "^0.1.21", - "lincd-server": "^0.1.39", - "lincd-server-utils": "^0.1.7", + "lincd-auth": "~1.0", + "lincd-jsonld": "~1.0", + "lincd-server": "~1.0", + "lincd-server-utils": "~1.0", "react": "^18.2", "react-dom": "^18.2", "react-error-boundary": "^3.1.3", @@ -82,7 +82,7 @@ "eslint-plugin-react": "latest", "eslint-plugin-react-hooks": "^4.6.0", "husky": "^8.0.0", - "lincd-cli": "^1.0", + "lincd-cli": "^1.1", "nodemon": "^2.0.22", "pinst": "^3.0.0", "pm2": "^5.3.0", diff --git a/defaults/app-with-backend/src/package.ts b/defaults/app-with-backend/src/package.ts index ebdab68..e1dc815 100644 --- a/defaults/app-with-backend/src/package.ts +++ b/defaults/app-with-backend/src/package.ts @@ -8,7 +8,6 @@ export const { linkedUtil, linkedOntology, registerPackageExport, - registerPackageModule, packageExports, packageName, } = linkedPackage('${hyphen_name}'); diff --git a/defaults/component.tsx b/defaults/component.tsx index 2f8c75b..2a38f98 100644 --- a/defaults/component.tsx +++ b/defaults/component.tsx @@ -1,12 +1,10 @@ import React from "react"; import "./${hyphen_name}.scss"; import {default as style} from "./${hyphen_name}.scss.json"; -import {registerPackageModule,linkedComponent} from '../package'; +import {linkedComponent} from '../package'; //TODO: replace SHAPE with an actual Shape class export const ${camel_name} = linkedComponent(SHAPE,({source}) => { return
; }); -//register all components in this file -registerPackageModule(module); \ No newline at end of file diff --git a/defaults/package/package.json b/defaults/package/package.json index 4eed609..9238650 100644 --- a/defaults/package/package.json +++ b/defaults/package/package.json @@ -33,6 +33,6 @@ "lincd-jsonld": "^0.1" }, "devDependencies": { - "lincd-cli": "^1.0" + "lincd-cli": "^1.1" } } diff --git a/defaults/package/src/index.ts b/defaults/package/src/index.ts index f1a7aae..0cf942a 100644 --- a/defaults/package/src/index.ts +++ b/defaults/package/src/index.ts @@ -1,3 +1,4 @@ +import './types'; import './ontologies/${hyphen_name}'; //SHAPES FIRST diff --git a/defaults/package/src/ontologies/example-ontology.ts b/defaults/package/src/ontologies/example-ontology.ts index cf6f999..87ea5b3 100644 --- a/defaults/package/src/ontologies/example-ontology.ts +++ b/defaults/package/src/ontologies/example-ontology.ts @@ -9,7 +9,14 @@ import * as _this from './${hyphen_name}'; * Load the data of this ontology into memory, thus adding the properties of the entities of this ontology to the local graph. */ export var loadData = () => { - return import('../data/${hyphen_name}.json').then((data) => JSONLD.parse(data)); + if (typeof module !== 'undefined' && typeof exports !== 'undefined') { + // CommonJS import + return import('../data/${hyphen_name}.json').then((data) => JSONLD.parse(data)); + } else { + // ESM import + //@ts-ignore + return import('../data/${hyphen_name}.json',{ assert: { type: "json" } }).then((data) => JSONLD.parse(data)); + } }; /** diff --git a/defaults/package/src/package.ts b/defaults/package/src/package.ts index 0fa63ee..d1fc062 100644 --- a/defaults/package/src/package.ts +++ b/defaults/package/src/package.ts @@ -6,7 +6,6 @@ export const { linkedShape, linkedUtil, linkedOntology, - registerPackageModule, registerPackageExport, packageExports, packageName, diff --git a/defaults/package/src/types.d.ts b/defaults/package/src/types.d.ts new file mode 100644 index 0000000..f0745b6 --- /dev/null +++ b/defaults/package/src/types.d.ts @@ -0,0 +1,9 @@ +declare module '*.module.css' { + const classes: {[key: string]: string}; + export default classes; +} + +declare module '*.module.scss' { + const classes: {[key: string]: string}; + export default classes; +} diff --git a/defaults/package/tsconfig-cjs.json b/defaults/package/tsconfig-cjs.json new file mode 100644 index 0000000..027ce41 --- /dev/null +++ b/defaults/package/tsconfig-cjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "lib/cjs" + } +} diff --git a/defaults/package/tsconfig-es5.json b/defaults/package/tsconfig-es5.json deleted file mode 100644 index 92d69c8..0000000 --- a/defaults/package/tsconfig-es5.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "sourceMap": true, - "target": "es5", - "outDir": "lib", - "declaration": false, - "esModuleInterop": true, - "resolveJsonModule": true, - "downlevelIteration": true, - "experimentalDecorators": true, - "skipLibCheck": true, - "types": ["node", "react", "react-dom"], - "jsx": "react", - "baseUrl": "./", - "rootDir": "src" - }, - "files": ["./src/index.ts"], - "include": ["./src/backend.ts"] -} diff --git a/defaults/package/tsconfig-esm.json b/defaults/package/tsconfig-esm.json new file mode 100644 index 0000000..b668eba --- /dev/null +++ b/defaults/package/tsconfig-esm.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "esnext", + "target": "es2018", + "outDir": "lib/esm", + "moduleResolution": "node" + } +} \ No newline at end of file diff --git a/defaults/package/tsconfig.json b/defaults/package/tsconfig.json index 0b2e6e9..e22ba4f 100644 --- a/defaults/package/tsconfig.json +++ b/defaults/package/tsconfig.json @@ -1,23 +1,16 @@ { "compilerOptions": { - "module": "commonjs", "sourceMap": true, - "target": "es6", - "moduleResolution": "node", - "outDir": "lib", "declaration": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, - "esModuleInterop": true, - "resolveJsonModule": true, "downlevelIteration": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "pretty": true, "jsx": "react", - "types": ["node", "react", "react-dom"], - "paths": { - "react": ["../node_modules/@types/react","../../../node_modules/@types/react"] - }, - "baseUrl": "./src" + "types": [] }, - "files": ["./src/index.ts"], + "files": ["./src/types.d.ts","./src/index.ts"], "include": ["./src/backend.ts"] } diff --git a/defaults/set-component.tsx b/defaults/set-component.tsx index c17dbf7..a3101b9 100644 --- a/defaults/set-component.tsx +++ b/defaults/set-component.tsx @@ -1,7 +1,7 @@ import React from "react"; import "./${hyphen_name}.scss"; import {default as style} from "./${hyphen_name}.scss.json"; -import {registerPackageModule,linkedSetComponent} from '../package'; +import {linkedSetComponent} from '../package'; //TODO: replace SHAPE with an actual Shape class export const ${camel_name} = linkedSetComponent(SHAPE,({sources}) => { @@ -10,7 +10,4 @@ export const ${camel_name} = linkedSetComponent(SHAPE,({sources}) => { return
; })} ; -}); - -//register all components in this file -registerPackageModule(module); \ No newline at end of file +}); \ No newline at end of file diff --git a/package.json b/package.json index 967e0f0..7c66685 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ }, "scripts": { "prepublishOnly": "npm exec build", - "build": "rimraf ./lib && yarn build-esm && yarn dual-package", + "build": "rimraf ./lib && yarn build-esm && yarn build-cjs && yarn dual-package", "build-cjs": "echo \"compiling CJS\" && yarn tsc -p tsconfig-cjs.json", "build-esm": "echo \"compiling ESM\" && yarn tsc -p tsconfig-esm.json", "dual-package": "echo 'adding package.json to each lib folder for dual output support' && yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json", @@ -85,12 +85,14 @@ "commander": "^11.0.0", "copy-webpack-plugin": "^12.0.2", "copyfiles": "^2.4.1", + "create-esm-loader": "^0.2.5", "css-loader": "^6.10.0", + "css-parse": "^2.0.0", "depcheck": "^1.4.7", "env-cmd": "^10.1.0", "find-nearest-package-json": "^2.0.1", "fs-extra": "^11.2.0", - "glob": "^10.3.10", + "glob": "^10.3.12", "grunt": "^1.6.1", "grunt-cli": "^1.4.3", "grunt-concurrent": "^3.0.0", @@ -101,10 +103,11 @@ "grunt-webpack": "^6.0.0", "license-info-webpack-plugin": "^3.0.0", "lincd": "^1.0", - "lincd-jsonld": "^0.1.22", - "lincd-modules": "^0.1", + "lincd-jsonld": "~1.0", + "lincd-modules": "~1.0", "load-grunt-tasks": "^5.1.0", "mini-css-extract-plugin": "^2.8.1", + "node-hook": "^1.0.0", "open": "^8.4.0", "ora": "^8.0.1", "postcss": "^8.4.35", @@ -131,7 +134,8 @@ "typescript": "^4.8.4", "webpack": "^5.90.3", "webpack-bundle-analyzer": "^4.10.1", - "webpack-license-plugin": "^4.4.2" + "webpack-license-plugin": "^4.4.2", + "webpack-typings-for-css": "^0.13.0" }, "devDependencies": { "husky": "^9.0.11", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 7bd1b06..06ce927 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -1,10 +1,10 @@ +import hook from 'node-hook'; import chalk from 'chalk'; -import {exec} from 'child_process'; +import { exec } from 'child_process'; import depcheck from 'depcheck'; -import {getEnvFile} from 'env-cmd/dist/get-env-vars.js'; +import { getEnvFile } from 'env-cmd/dist/get-env-vars.js'; import fs from 'fs-extra'; -import ts from 'typescript'; -import path from 'path'; +import path,{ dirname } from 'path'; import { debugInfo, execp, @@ -17,13 +17,12 @@ import { needsRebuilding, } from './utils.js'; -import {statSync} from 'fs'; -import {PackageDetails} from 'interfaces'; -import {findNearestPackageJson} from 'find-nearest-package-json'; -import {LinkedFileStorage} from 'lincd/utils/LinkedFileStorage'; +import { statSync } from 'fs'; +import { PackageDetails } from 'interfaces'; +import { findNearestPackageJson } from 'find-nearest-package-json'; +import { LinkedFileStorage } from 'lincd/utils/LinkedFileStorage'; // import pkg from 'lincd/utils/LinkedFileStorage'; // const { LinkedFileStorage } = pkg; - // const config = require('lincd-server/site.webpack.config'); import { glob } from 'glob'; import webpack from 'webpack'; @@ -33,39 +32,41 @@ import copyfiles from 'copyfiles'; import ora from 'ora'; var variables = {}; -export const createApp = async (name, basePath = process.cwd()) => { - if (!name) { +export const createApp = async (name,basePath = process.cwd()) => { + if (!name) + { console.warn('Please provide a name as the first argument'); } - let {hyphenName, camelCaseName, underscoreName} = setNameVariables(name); + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(name); - let targetFolder = path.join(basePath, hyphenName); - if (!fs.existsSync(targetFolder)) { + let targetFolder = path.join(basePath,hyphenName); + if (!fs.existsSync(targetFolder)) + { fs.mkdirSync(targetFolder); } fs.copySync( - path.join(__dirname, '..', 'defaults', 'app-with-backend'), + path.join(__dirname,'..','defaults','app-with-backend'), targetFolder, ); //make sure the data folder exists (even though its empty).. copying empty folders does not work with fs.copySync - fs.mkdirSync(path.join(targetFolder, 'data'), {recursive: true}); - fs.mkdirSync(path.join(targetFolder, 'data/uploads/resized'), { + fs.mkdirSync(path.join(targetFolder,'data'),{ recursive: true }); + fs.mkdirSync(path.join(targetFolder,'data/uploads/resized'),{ recursive: true, }); fs.renameSync( - path.join(targetFolder, 'gitignore.template'), - path.join(targetFolder, '.gitignore'), + path.join(targetFolder,'gitignore.template'), + path.join(targetFolder,'.gitignore'), ); fs.renameSync( - path.join(targetFolder, 'yarnrc.yml.template'), - path.join(targetFolder, '.yarnrc.yml'), + path.join(targetFolder,'yarnrc.yml.template'), + path.join(targetFolder,'.yarnrc.yml'), ); // fs.copySync(path.join(__dirname, '..', 'defaults', 'app'), targetFolder); - log("Creating new LINCD application '" + name + "'"); + log('Creating new LINCD application \'' + name + '\''); //replace variables in some copied files await replaceVariablesInFolder(targetFolder); @@ -75,7 +76,7 @@ export const createApp = async (name, basePath = process.cwd()) => { ? 'export NODE_OPTIONS="--no-network-family-autoselection" && yarn install' : 'npm install'; - await execp(`cd ${hyphenName} && ${installCommand}`, true).catch((err) => { + await execp(`cd ${hyphenName} && ${installCommand}`,true).catch((err) => { console.warn('Could not install dependencies or start application'); }); @@ -87,90 +88,110 @@ export const createApp = async (name, basePath = process.cwd()) => { ); }; -function logHelp() { +function logHelp() +{ execp('yarn exec lincd help'); } -function log(...messages) { +function log(...messages) +{ messages.forEach((message) => { console.log(chalk.cyan('Info: ') + message); }); } -function progressUpdate(message) { +function progressUpdate(message) +{ process.stdout.write( ' \r', ); process.stdout.write(message + '\r'); } -export function warn(...messages) { +export function warn(...messages) +{ messages.forEach((message) => { - console.log(chalk.magenta('Warning: ') + message); + console.log(chalk.redBright('Warning: ') + message); // console.log(chalk.red(message)); }); } -export function developPackage(target, mode) { +export function developPackage(target,mode) +{ if (!target) target = 'es6'; if (mode !== 'production') mode = ''; else if (target !== 'es6') log('target must be es6 when developing for production'); - if (target == 'es5' || target == 'es6') { + if (target == 'es5' || target == 'es6') + { // log('> Starting continuous development build for '+target+' target') log('starting continuous development build'); log( 'grunt dev' + - (target ? '-' + target : '') + - (mode ? '-' + mode : '') + - ' --color', + (target ? '-' + target : '') + + (mode ? '-' + mode : '') + + ' --color', ); var command = exec( 'grunt dev' + - (target ? '-' + target : '') + - (mode ? '-' + mode : '') + - ' --color', + (target ? '-' + target : '') + + (mode ? '-' + mode : '') + + ' --color', ); command.stdout.pipe(process.stdout); command.stderr.pipe(process.stderr); - } else { + } + else + { console.warn('unknown build target. Use es5 or es6'); } } -function checkWorkspaces(rootPath, workspaces, res) { +function checkWorkspaces(rootPath,workspaces,res) +{ // console.log('checking workspaces at '+rootPath+": "+workspaces.toString()); - if (workspaces.packages) { + if (workspaces.packages) + { workspaces = workspaces.packages; } workspaces.forEach((workspace) => { - let workspacePath = path.join(rootPath, workspace.replace('/*', '')); - if (workspace.indexOf('/*') !== -1) { + let workspacePath = path.join(rootPath,workspace.replace('/*','')); + if (workspace.indexOf('/*') !== -1) + { // console.log(workspacePath); - if (fs.existsSync(workspacePath)) { + if (fs.existsSync(workspacePath)) + { let folders = fs.readdirSync(workspacePath); folders.forEach((folder) => { - if (folder !== './' && folder !== '../') { - checkPackagePath(rootPath, path.join(workspacePath, folder), res); + if (folder !== './' && folder !== '../') + { + checkPackagePath(rootPath,path.join(workspacePath,folder),res); } }); } - } else { - checkPackagePath(rootPath, workspacePath, res); + } + else + { + checkPackagePath(rootPath,workspacePath,res); } }); } -function checkPackagePath(rootPath, packagePath, res) { - let packageJsonPath = path.join(packagePath, 'package.json'); +function checkPackagePath(rootPath,packagePath,res) +{ + let packageJsonPath = path.join(packagePath,'package.json'); // console.log('checking '+packagePath); - if (fs.existsSync(packageJsonPath)) { - var pack = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + if (fs.existsSync(packageJsonPath)) + { + var pack = JSON.parse(fs.readFileSync(packageJsonPath,'utf8')); //some packages are not true lincd packages, but we still want them to be re-built automatically. This is what lincd_util is for - if (pack && pack.workspaces) { - checkWorkspaces(packagePath, pack.workspaces, res); - } else if (pack && pack.lincd === true) { + if (pack && pack.workspaces) + { + checkWorkspaces(packagePath,pack.workspaces,res); + } + else if (pack && pack.lincd === true) + { res.push({ path: packagePath, packageName: pack.name, @@ -187,14 +208,16 @@ function runOnPackagesGroupedByDependencies( ) => (pkg: PackageDetails) => Promise, onStackEnd, sync = false, -) { - let dependencies: Map = new Map(); +) +{ + let dependencies: Map = new Map(); //get dependencies of each package let leastDependentPackage; lincdPackages.forEach((pkg) => { var pack = getPackageJSON(pkg.path); - if (pack) { + if (pack) + { //get lincd related dependencies and get the actual package details from the package map by removing '@dacore/' from the package name let packageDependencies = Object.keys(pack.dependencies) .filter((dependency) => lincdPackages.has(dependency)) @@ -204,11 +227,11 @@ function runOnPackagesGroupedByDependencies( : dependency; }); // console.log(package.packageName,packageDependencies.map()) - dependencies.set(pkg, packageDependencies); + dependencies.set(pkg,packageDependencies); } }); - dependencies.forEach((PackageDependencies, pkg) => { + dependencies.forEach((PackageDependencies,pkg) => { if ( !PackageDependencies.some((dependency) => { return ( @@ -216,21 +239,39 @@ function runOnPackagesGroupedByDependencies( lincdPackages.has(dependency.packageName) ); }) - ) { + ) + { leastDependentPackage = pkg; } }); let startStack: PackageDetails[] = [leastDependentPackage]; - const runPackage = (runFunction, pck) => { + const runPackage = (runFunction,pck) => { return runFunction(pck) - .catch(({error, stdout, stderr}) => { - warn( - 'Uncaught exception whilst running parallel function on ' + + .catch((errorObj) => { + if (errorObj.error) + { + let { error,stdout,stderr } = errorObj; + warn( + 'Uncaught exception whilst running parallel function on ' + pck.packageName, - error.message, - ); + error?.message ? error.message : error?.toString(), + // stdout, + // stderr, + ); + } + else + { + warn( + 'Uncaught exception whilst running parallel function on ' + + pck.packageName, + errorObj?.toString(), + // stdout, + // stderr, + ); + process.exit(); + } // warn(chalk.red(pck.packageName+' failed:')); // console.log(stdout); }) @@ -243,21 +284,24 @@ function runOnPackagesGroupedByDependencies( let done: Set = new Set(); let results = []; let runStack = async (stack) => { - let runFunction = onBuildStack(stack, dependencies); + let runFunction = onBuildStack(stack,dependencies); let stackPromise: Promise; - if (sync) { + if (sync) + { //build the stack in parallel stackPromise = Promise.resolve(true); stack.forEach((pck) => { stackPromise = stackPromise.then(() => { - return runPackage(runFunction, pck); + return runPackage(runFunction,pck); }); }); - } else { + } + else + { //build the stack in parallel stackPromise = Promise.all( stack.map((pck) => { - return runPackage(runFunction, pck); + return runPackage(runFunction,pck); }), ); } @@ -283,34 +327,36 @@ function runOnPackagesGroupedByDependencies( (done.has(dependency) || !lincdPackages.has(dependency.packageName)) ); }) - ) { + ) + { stack.push(pkg); } }); - if (stack.length <= 0 && done.size < lincdPackages.size) { + if (stack.length <= 0 && done.size < lincdPackages.size) + { console.log( chalk.red( 'Only ' + - done.size + - ' out of ' + - lincdPackages.size + - ' packages have been built', + done.size + + ' out of ' + + lincdPackages.size + + ' packages have been built', ), ); console.log( 'ALL remaining packages have dependencies that have not been met. This may point to ' + - chalk.red('circular dependencies.'), + chalk.red('circular dependencies.'), ); console.log( 'Already built: ' + - Array.from(done) - .map((p) => chalk.green(p.packageName)) - .join(', '), + Array.from(done) + .map((p) => chalk.green(p.packageName)) + .join(', '), ); console.log( chalk.blue('\nTo solve this issue') + - ': find the circular dependencies below and fix the dependencies:\n\n', + ': find the circular dependencies below and fix the dependencies:\n\n', ); //TODO: actually find and name the packages that have circular dependencies // let circular = []; @@ -330,25 +376,26 @@ function runOnPackagesGroupedByDependencies( // }); lincdPackages.forEach((pkg) => { let deps = dependencies.get(pkg); - if (!done.has(pkg)) { + if (!done.has(pkg)) + { console.log( chalk.red(pkg.packageName) + - ' has not been built yet. Unbuilt dependencies:\n' + - deps - .filter((dependency) => { - return !Array.from(done).some((p) => { - // console.log(p.packageName,dependency.packageName,p===dependency) - return p === dependency; - }); - }) - .map((p) => - chalk.red( - '\t- ' + - (p?.packageName ? p.packageName : p.toString()) + - '\n', - ), - ) - .join(' '), + ' has not been built yet. Unbuilt dependencies:\n' + + deps + .filter((dependency) => { + return !Array.from(done).some((p) => { + // console.log(p.packageName,dependency.packageName,p===dependency) + return p === dependency; + }); + }) + .map((p) => + chalk.red( + '\t- ' + + (p?.packageName ? p.packageName : p.toString()) + + '\n', + ), + ) + .join(' '), ); // console.log(chalk.red(pkg.packageName)+' has not been built yet. Built dependencies:\n' + deps.filter(dependency => { // return Array.from(done).some(p => p.packageName === pkg.packageName) @@ -359,10 +406,13 @@ function runOnPackagesGroupedByDependencies( } //if more to be built, iterate - if (stack.length > 0) { + if (stack.length > 0) + { return runStack(stack); - } else { - onStackEnd(dependencies, results.filter(Boolean)); + } + else + { + onStackEnd(dependencies,results.filter(Boolean)); } }; @@ -370,7 +420,8 @@ function runOnPackagesGroupedByDependencies( runStack(startStack); } -function hasDependency(pkg, childPkg, dependencies, depth = 1) { +function hasDependency(pkg,childPkg,dependencies,depth = 1) +{ console.log( 'Does ' + pkg.packageName + ' have dep ' + childPkg.packageName + ' ?', ); @@ -386,10 +437,11 @@ function hasDependency(pkg, childPkg, dependencies, depth = 1) { // return dependency === childPkg; return ( dependency === childPkg || - hasDependency(dependency, childPkg, dependencies, depth++) + hasDependency(dependency,childPkg,dependencies,depth++) ); }) - ) { + ) + { console.log('##YES'); return true; } @@ -397,7 +449,8 @@ function hasDependency(pkg, childPkg, dependencies, depth = 1) { return false; } -export function buildAll(options) { +export function buildAll(options) +{ console.log( 'Building all LINCD packages of this repository in order of dependencies', ); @@ -415,7 +468,8 @@ export function buildAll(options) { // process.exit(); //option to start from a specific package in the stack - if (from) { + if (from) + { startFrom = from; //if we have a startFrom, then we havnt started the build process yet building = startFrom ? false : true; @@ -449,8 +503,9 @@ export function buildAll(options) { // let packagesLeft = lincdPackages.size - done.size; runOnPackagesGroupedByDependencies( lincdPackages, - (packageGroup, dependencies) => { - if (done.size > 0) { + (packageGroup,dependencies) => { + if (done.size > 0) + { debugInfo( chalk.magenta( '\n-------\nThese packages are next, since all their dependencies have now been build:', @@ -465,95 +520,117 @@ export function buildAll(options) { let command; let skipping = false; //if we're skipping builds until a certain package - if (!building) { + if (!building) + { //if the package name matches the package we're supposed to start from then start building packages - if (pkg.packageName == startFrom || pkg.packageName == startFrom) { + if (pkg.packageName == startFrom || pkg.packageName == startFrom) + { building = true; } //else still waiting for the package - else { + else + { log(chalk.blue('skipping ' + pkg.packageName)); command = Promise.resolve(true); skipping = true; } } //unless told otherwise, build the package - if (!command) { - command = execPromise( - 'cd ' + pkg.path + ' && yarn exec lincd build', - // (target ? ' ' + target : '') + - // (target2 ? ' ' + target2 : ''), - false, - false, - {}, - false, - ); + if (!command) + { + command = buildPackage(null,null,path.join(process.cwd(),pkg.path),false); + // command = execPromise( + // 'cd ' + pkg.path + ' && yarn exec lincd build', + // // (target ? ' ' + target : '') + + // // (target2 ? ' ' + target2 : ''), + // false, + // false, + // {}, + // false, + // ); log(chalk.cyan('Building ' + pkg.packageName)); process.stdout.write(packagesLeft + ' packages left\r'); } - return command - .catch(({error, stdout, stderr}) => { + return command.then(success => { + + //undefined result means it failed + if (typeof success === 'undefined') + { + // .catch(({ error,stdout,stderr }) => { //this prints out the webpack output, including the build errors - warn('Failed to build ' + pkg.packageName); - console.log(stdout); + // warn('Failed to build ' + pkg.packageName); + // console.log(stdout); failedModules.push(pkg.packageName); - let dependentModules = getDependentPackages(dependencies, pkg); - if (dependentModules.length > 0) { - printBuildResults(failedModules, done); + let dependentModules = getDependentPackages(dependencies,pkg); + if (dependentModules.length > 0) + { + printBuildResults(failedModules,done); console.log( - chalk.magenta( - 'Stopping build process because an error occurred whilst building ' + - pkg.packageName + - ', which ' + - dependentModules.length + - ' other packages depend on.', - ), + 'Stopping build process because an error occurred whilst building ' + + pkg.packageName + + ', which ' + + dependentModules.length + + ' other packages depend on.', ); //"+dependentModules.map(d => d.packageName).join(", "))); - console.log( - chalk.cyanBright('tip ') + - 'Run ' + - chalk.green(`lincd build-all --from=${pkg.packageName}`) + - ' to build only the remaining packages', + log( + 'Run ' + + chalk.greenBright(`lincd build-all --from=${pkg.packageName}`) + + ' to build only the remaining packages', ); //"+dependentModules.map(d => d.packageName).join(", "))); process.exit(1); } - }) - .then((res) => { - if (!skipping) { - log(chalk.green('Built ' + pkg.packageName)); + } + else //true is successful build, false is successful but with warnings + { + //successful build + // }) + // .then((res) => { + if (!skipping) + { + log(chalk.green('Built ' + pkg.packageName)+(success === false ? chalk.redBright(' (with warnings)') : '')); } done.add(pkg); packagesLeft--; // log(chalk.magenta(packagesLeft + ' packages left')); process.stdout.write(packagesLeft + ' packages left\r'); - if (packagesLeft == 0) { - printBuildResults(failedModules, done); - if (failedModules.length > 0) { + if (packagesLeft == 0) + { + printBuildResults(failedModules,done); + if (failedModules.length > 0) + { process.exit(1); } } - return res; - }); + return success; + } + }).catch(err => { + console.log(err); + }) }; }, (dependencies) => { //if no more packages to build but we never started building... - if (!building) { + if (!building) + { console.log( chalk.red( 'Could not find the package to start from. Please provide a correct package name or package name to build from', ), ); - } else { + } + else + { //Detecting cyclical dependencies that caused some packages not to be build let first = true; lincdPackages.forEach((pkg) => { - if (!done.has(pkg)) { + if (!done.has(pkg)) + { let deps = dependencies.get(pkg); - if (first) { + if (first) + { console.log( chalk.red( 'CYCLICAL DEPENDENCIES? - could not build some packages because they depend on each other.', @@ -564,26 +641,27 @@ export function buildAll(options) { //print the cyclical dependencies console.log( chalk.red(pkg.packageName) + - ' depends on ' + - deps - .filter((dependency) => { - return typeof dependency !== 'string'; - }) - .map((d: PackageDetails) => { - return done.has(d) - ? d.packageName - : chalk.red(d.packageName); - }) - .join(', '), + ' depends on ' + + deps + .filter((dependency) => { + return typeof dependency !== 'string'; + }) + .map((d: PackageDetails) => { + return done.has(d) + ? d.packageName + : chalk.red(d.packageName); + }) + .join(', '), ); //also print some information why these packages have not been moved into the stack let stringDependencies = deps.filter((d) => typeof d === 'string'); - if (stringDependencies.length > 0) { + if (stringDependencies.length > 0) + { console.log( chalk.red( 'And it depends on these package(s) - which seem not to be proper packages :' + - stringDependencies.join(', '), + stringDependencies.join(', '), ), ); console.log( @@ -600,10 +678,12 @@ export function buildAll(options) { ); } -function getDependentPackages(dependencies, pkg): PackageDetails[] { +function getDependentPackages(dependencies,pkg): PackageDetails[] +{ let dependentModules: PackageDetails[] = []; - dependencies.forEach((dModuleDependencies, dModule) => { - if (dModuleDependencies.indexOf(pkg) !== -1) { + dependencies.forEach((dModuleDependencies,dModule) => { + if (dModuleDependencies.indexOf(pkg) !== -1) + { dependentModules.push(dModule); } }); @@ -615,38 +695,46 @@ function getDependentPackages(dependencies, pkg): PackageDetails[] { * Returns a map of the packages that this repository manages (so no packages found through the workspaces who's path contains ../ ) * @param rootPath */ -function getLocalLincdPackageMap(rootPath = './'): Map { +function getLocalLincdPackageMap(rootPath = './'): Map +{ let map = new Map(); getLincdPackages(rootPath).forEach((pkg) => { - if (pkg.path.indexOf('../') === -1 && pkg.path.indexOf('..\\') === -1) { + if (pkg.path.indexOf('../') === -1 && pkg.path.indexOf('..\\') === -1) + { // console.log(package.path); - map.set(pkg.packageName, pkg); + map.set(pkg.packageName,pkg); } }); return map; } -function getLocalLincdModules(rootPath = './'): PackageDetails[] { +function getLocalLincdModules(rootPath = './'): PackageDetails[] +{ return getLincdPackages(rootPath).filter((pkg) => { return pkg.path.indexOf('..\\') === -1; }); } -export function getLincdPackages(rootPath = process.cwd()): PackageDetails[] { +export function getLincdPackages(rootPath = process.cwd()): PackageDetails[] +{ let pack = getPackageJSON(); - if (!pack || !pack.workspaces) { - for (let i = 0; i <= 3; i++) { - rootPath = path.join(process.cwd(), ...Array(i).fill('..')); + if (!pack || !pack.workspaces) + { + for (let i = 0; i <= 3; i++) + { + rootPath = path.join(process.cwd(),...Array(i).fill('..')); pack = getPackageJSON(rootPath); - if (pack && pack.workspaces) { + if (pack && pack.workspaces) + { // log('Found workspace at '+packagePath); break; } } } - if (!pack || !pack.workspaces) { + if (!pack || !pack.workspaces) + { warn( chalk.red( 'Could not find package workspaces. Make sure you run this command from a yarn workspace.', @@ -658,33 +746,38 @@ export function getLincdPackages(rootPath = process.cwd()): PackageDetails[] { // console.log(pack.workspaces); let res = []; - checkWorkspaces(rootPath, pack.workspaces, res); + checkWorkspaces(rootPath,pack.workspaces,res); return res; } -function setVariable(name, replacement) { +function setVariable(name,replacement) +{ //prepare name for regexp - name = name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + name = name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,'\\$&'); variables[name] = replacement; } var replaceVariablesInFile = async (filePath: string) => { - var fileContent = await fs.readFile(filePath, 'utf8').catch((err) => { + var fileContent = await fs.readFile(filePath,'utf8').catch((err) => { console.warn(chalk.red('Could not read file ' + filePath)); }); - if (fileContent) { + if (fileContent) + { var newContent = replaceCurlyVariables(fileContent); - return fs.writeFile(filePath, newContent); - } else { + return fs.writeFile(filePath,newContent); + } + else + { return Promise.resolve(); } }; -var replaceCurlyVariables = function (string) { +var replaceCurlyVariables = function(string) { // var reg = new RegExp('\\$\\{'+key+'\\}','g'); - for (var key in variables) { + for (var key in variables) + { string = string.replace( - new RegExp('\\$\\{' + key + '\\}', 'g'), + new RegExp('\\$\\{' + key + '\\}','g'), variables[key], ); } @@ -694,9 +787,10 @@ var replaceCurlyVariables = function (string) { const capitalize = (str) => str.charAt(0).toUpperCase() + str.toLowerCase().slice(1); const camelCase = (str) => { - let string = str.replace(/[^A-Za-z0-9]/g, ' ').split(' '); - if (string.length > 1) { - return string.reduce((result, word) => result + capitalize(word)); + let string = str.replace(/[^A-Za-z0-9]/g,' ').split(' '); + if (string.length > 1) + { + return string.reduce((result,word) => result + capitalize(word)); } return str; }; @@ -706,24 +800,26 @@ export const createOntology = async ( uriBase?, basePath = process.cwd(), ) => { - if (!prefix) { + if (!prefix) + { console.warn('Please provide a suggested prefix as the first argument'); return; } let sourceFolder = getSourceFolder(basePath); - let targetFolder = ensureFolderExists(sourceFolder, 'ontologies'); + let targetFolder = ensureFolderExists(sourceFolder,'ontologies'); - if (!uriBase) { + if (!uriBase) + { uriBase = 'http://lincd.org/ont/' + prefix + '/'; } - setVariable('uri_base', uriBase); + setVariable('uri_base',uriBase); - let {hyphenName, camelCaseName, underscoreName} = setNameVariables(prefix); + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(prefix); //copy ontology accessor file - log("Creating files for ontology '" + prefix + "'"); - let targetFile = path.join(targetFolder, hyphenName + '.ts'); + log('Creating files for ontology \'' + prefix + '\''); + let targetFile = path.join(targetFolder,hyphenName + '.ts'); fs.copySync( path.join( __dirname, @@ -775,18 +871,19 @@ export const createOntology = async ( targetDataFile2, ); - await replaceVariablesInFiles(targetFile, targetDataFile, targetDataFile2); + await replaceVariablesInFiles(targetFile,targetDataFile,targetDataFile2); log( `Prepared a new ontology data files in ${chalk.magenta( - targetDataFile.replace(basePath, ''), + targetDataFile.replace(basePath,''), )}`, `And an ontology accessor file in ${chalk.magenta( - targetFile.replace(basePath, ''), + targetFile.replace(basePath,''), )}`, ); //if this is not a lincd app (but a lincd package instead) - if (!sourceFolder.includes('frontend')) { + if (!sourceFolder.includes('frontend')) + { //then also add an import to index let indexPath = addLineToIndex( `import './ontologies/${hyphenName}';`, @@ -795,48 +892,63 @@ export const createOntology = async ( log(`Added an import of this file from ${chalk.magenta(indexPath)}`); } }; -const addLineToIndex = function (line, insertMatchString: string) { +const addLineToIndex = function(line,insertMatchString: string,root: string = process.cwd(),insertAtStart: boolean = false) { //import ontology in index - let indexPath = ['index.ts', 'index.tsx'] - .map((f) => path.join('src', f)) + let indexPath = ['index.ts','index.tsx'] + .map((f) => path.join(root,'src',f)) .find((indexFileName) => { return fs.existsSync(indexFileName); }); - if (indexPath) { - let indexContents = fs.readFileSync(indexPath, 'utf-8'); + if (indexPath) + { + let indexContents = fs.readFileSync(indexPath,'utf-8'); let lines = indexContents.split(/\n/g); let newContents; - for (var key in lines) { - if (lines[key].indexOf(insertMatchString) !== -1) { - //remove lines after this line and insert new line in its place + for (var key in lines) + { + //if the match string is found + if (lines[key].indexOf(insertMatchString) !== -1) + { + //add the new line after this line lines[key] += `\n${line}`; newContents = lines.join('\n'); // log("Found at "+key,lines,newContents); break; } } - if (!newContents) { - newContents = indexContents + `\n${line}`; + if (!newContents) + { + if (insertAtStart) + { + newContents = `${line}\n${indexContents}`; + } + else + { + newContents = `${indexContents}\n${line}`; + } // log("Added at end",newContents); } - fs.writeFileSync(indexPath, newContents); + fs.writeFileSync(indexPath,newContents); } return indexPath; }; -const replaceVariablesInFiles = function (...files: string[]) { +const replaceVariablesInFiles = function(...files: string[]) { return Promise.all( files.map((file) => { return replaceVariablesInFile(file); }), ); }; -const replaceVariablesInFolder = function (folder: string) { +const replaceVariablesInFolder = function(folder: string) { //get all files in folder, including files that start with a dot - (glob as any)(folder + '/**/*', {dot: true, nodir: true}, async function (err, files) { - if (err) { - console.log('Error', err); - } else { + (glob as any)(folder + '/**/*',{ dot: true,nodir: true },async function(err,files) { + if (err) + { + console.log('Error',err); + } + else + { // console.log(files); await Promise.all( files.map((file) => { @@ -847,13 +959,13 @@ const replaceVariablesInFolder = function (folder: string) { }); }; -const replaceVariablesInFilesWithRoot = function ( +const replaceVariablesInFilesWithRoot = function( root: string, ...files: string[] ) { - return replaceVariablesInFiles(...files.map((f) => path.join(root, f))); + return replaceVariablesInFiles(...files.map((f) => path.join(root,f))); }; -const hasYarnInstalled = async function () { +const hasYarnInstalled = async function() { let version = (await execPromise('yarn --version').catch((err) => { console.log('yarn probably not working'); return ''; @@ -861,11 +973,12 @@ const hasYarnInstalled = async function () { return version.toString().match(/[0-9]+/); }; -const ensureFolderExists = function (...folders: string[]) { +const ensureFolderExists = function(...folders: string[]) { let target; folders.forEach((folder) => { - target = target ? path.join(target, folder) : path.join(folder); - if (!fs.existsSync(target)) { + target = target ? path.join(target,folder) : path.join(folder); + if (!fs.existsSync(target)) + { fs.mkdirSync(target); } }); @@ -884,82 +997,105 @@ const ensureFolderExists = function (...folders: string[]) { return targetFolder;*/ }; -export const setNameVariables = function (name) { - let hyphenName = name.replace(/[-_\s]+/g, '-'); +export const setNameVariables = function(name) { + let hyphenName = name.replace(/[-_\s]+/g,'-'); let camelCaseName = camelCase(name); //some-package --> someModule - let underscoreName = name.replace(/[-\s]+/g, '_'); - let plainName = name.replace(/[-\s]+/g, ''); + let underscoreName = name.replace(/[-\s]+/g,'_'); + let plainName = name.replace(/[-\s]+/g,''); //longer similar variables names should come before the shorter ones - setVariable('underscore_name', underscoreName); - setVariable('hyphen_name', hyphenName); - setVariable('camel_name', camelCaseName); - setVariable('name', name); - setVariable('plain_name', plainName); + setVariable('underscore_name',underscoreName); + setVariable('hyphen_name',hyphenName); + setVariable('camel_name',camelCaseName); + setVariable('name',name); + setVariable('plain_name',plainName); - return {hyphenName, camelCaseName, underscoreName, plainName}; + return { hyphenName,camelCaseName,underscoreName,plainName }; }; -function getSourceFolder(basePath = process.cwd()) { +function getSourceFolder(basePath = process.cwd()) +{ //LINCD App - if (fs.existsSync(path.join(basePath, 'frontend', 'src'))) { - return path.join(basePath, 'frontend', 'src'); + if (fs.existsSync(path.join(basePath,'frontend','src'))) + { + return path.join(basePath,'frontend','src'); } //LINCD package - if (fs.existsSync(path.join(basePath, 'src'))) { - return path.join(basePath, 'src'); - } else { + if (fs.existsSync(path.join(basePath,'src'))) + { + return path.join(basePath,'src'); + } + else + { console.warn('Cannot find source folder'); - return path.join(basePath, 'src'); + return path.join(basePath,'src'); } } -export const createShape = async (name, basePath = process.cwd()) => { +/** + * get __dirname for either ESM/CJS + */ +export const getScriptDir = () => { + // @ts-ignore + if (typeof __dirname !== 'undefined') + { + // @ts-ignore + return __dirname; + } + else + { + // @ts-ignore + return dirname(import.meta.url).replace('file:/',''); + } + +}; +export const createShape = async (name,basePath = process.cwd()) => { let sourceFolder = getSourceFolder(basePath); - let targetFolder = ensureFolderExists(sourceFolder, 'shapes'); - let {hyphenName, camelCaseName, underscoreName} = setNameVariables(name); + let targetFolder = ensureFolderExists(sourceFolder,'shapes'); + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(name); //copy default shape file // log("Creating files for shape '" + name + "'"); - let targetFile = path.join(targetFolder, hyphenName + '.ts'); - fs.copySync(path.join(__dirname, '..', 'defaults', 'shape.ts'), targetFile); + let targetFile = path.join(targetFolder,hyphenName + '.ts'); + fs.copySync(path.join(__dirname,'..','defaults','shape.ts'),targetFile); //replace variables in some of the copied files await replaceVariablesInFiles(targetFile); log( `Created a new shape class template in ${chalk.magenta( - targetFile.replace(basePath, ''), + targetFile.replace(basePath,''), )}`, ); //if this is NOT a lincd app (but a lincd package) let indexPath; - if (!sourceFolder.includes('frontend')) { - indexPath = addLineToIndex(`import './shapes/${hyphenName}';`, 'shapes'); + if (!sourceFolder.includes('frontend')) + { + indexPath = addLineToIndex(`import './shapes/${hyphenName}';`,'shapes'); log(`Added an import of this file from ${chalk.magenta(indexPath)}`); } }; -export const createSetComponent = async (name, basePath = process.cwd()) => { - let targetFolder = ensureFolderExists(basePath, 'src', 'components'); - let {hyphenName, camelCaseName, underscoreName} = setNameVariables(name); +export const createSetComponent = async (name,basePath = process.cwd()) => { + let targetFolder = ensureFolderExists(basePath,'src','components'); + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(name); //copy default shape file - log("Creating files for set component '" + name + "'"); - let targetFile = path.join(targetFolder, hyphenName + '.tsx'); + log('Creating files for set component \'' + name + '\''); + let targetFile = path.join(targetFolder,hyphenName + '.tsx'); fs.copySync( - path.join(__dirname, '..', 'defaults', 'set-component.tsx'), + path.join(__dirname,'..','defaults','set-component.tsx'), targetFile, ); - let targetFile2 = path.join(targetFolder, hyphenName + '.scss'); + let targetFile2 = path.join(targetFolder,hyphenName + '.scss'); fs.copySync( - path.join(__dirname, '..', 'defaults', 'component.scss'), + path.join(__dirname,'..','defaults','component.scss'), targetFile2, ); //replace variables in some of the copied files - await replaceVariablesInFiles(targetFile, targetFile2); + await replaceVariablesInFiles(targetFile,targetFile2); let indexPath = addLineToIndex( `import './components/${hyphenName}';`, @@ -968,46 +1104,47 @@ export const createSetComponent = async (name, basePath = process.cwd()) => { log( `Created a new set component in ${chalk.magenta( - targetFile.replace(basePath, ''), + targetFile.replace(basePath,''), )}`, `Created a new stylesheet in ${chalk.magenta( - targetFile2.replace(basePath, ''), + targetFile2.replace(basePath,''), )}`, `Added an import of this file from ${chalk.magenta(indexPath)}`, ); }; -export const createComponent = async (name, basePath = process.cwd()) => { +export const createComponent = async (name,basePath = process.cwd()) => { let sourceFolder = getSourceFolder(basePath); - let targetFolder = ensureFolderExists(sourceFolder, 'components'); - let {hyphenName, camelCaseName, underscoreName} = setNameVariables(name); + let targetFolder = ensureFolderExists(sourceFolder,'components'); + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(name); //copy default shape file - log("Creating files for component '" + name + "'"); - let targetFile = path.join(targetFolder, hyphenName + '.tsx'); + log('Creating files for component \'' + name + '\''); + let targetFile = path.join(targetFolder,hyphenName + '.tsx'); fs.copySync( - path.join(__dirname, '..', 'defaults', 'component.tsx'), + path.join(__dirname,'..','defaults','component.tsx'), targetFile, ); - let targetFile2 = path.join(targetFolder, hyphenName + '.scss'); + let targetFile2 = path.join(targetFolder,hyphenName + '.scss'); fs.copySync( - path.join(__dirname, '..', 'defaults', 'component.scss'), + path.join(__dirname,'..','defaults','component.scss'), targetFile2, ); //replace variables in some of the copied files - await replaceVariablesInFiles(targetFile, targetFile2); + await replaceVariablesInFiles(targetFile,targetFile2); log( `Created a new component template in ${chalk.magenta( - targetFile.replace(basePath, ''), + targetFile.replace(basePath,''), )}`, `Created component stylesheet template in ${chalk.magenta( - targetFile2.replace(basePath, ''), + targetFile2.replace(basePath,''), )}`, ); //if this is not a lincd app (but a lincd package instead) - if (!sourceFolder.includes('frontend')) { + if (!sourceFolder.includes('frontend')) + { //then also add an import to index let indexPath = addLineToIndex( `import './components/${hyphenName}';`, @@ -1023,20 +1160,23 @@ export const createComponent = async (name, basePath = process.cwd()) => { export const checkImports = async ( sourceFolder: string = getSourceFolder(), depth: number = 0, // Used to check if the import is outside of the src folder - invalidImports: Map = new Map(), + invalidImports: Map = new Map(), ) => { const dir = fs.readdirSync(sourceFolder); // Start checking each file in the source folder - for (const file of dir) { - const filename = path.join(sourceFolder, file); + for (const file of dir) + { + const filename = path.join(sourceFolder,file); // File is either a directory, or not a .ts(x) // INFO: For future use - if this part fails, it could be due to user permissions // i.e. the program not having access to check the file metadata - if (!filename.match(/\.tsx?$/)) { - if (statSync(filename).isDirectory()) { - await checkImports(filename, depth + 1, invalidImports); + if (!filename.match(/\.tsx?$/)) + { + if (statSync(filename).isDirectory()) + { + await checkImports(filename,depth + 1,invalidImports); } // Ignore all files that aren't one of the following: @@ -1051,9 +1191,11 @@ export const checkImports = async ( ); lincdImports.forEach((i) => { - if (!isValidLINCDImport(i, depth)) { - if (!invalidImports.has(filename)) { - invalidImports.set(filename, []); + if (!isValidLINCDImport(i,depth)) + { + if (!invalidImports.has(filename)) + { + invalidImports.set(filename,[]); } invalidImports.get(filename).push(i); @@ -1063,31 +1205,33 @@ export const checkImports = async ( let res = ''; // All recursion must have finished, display any errors - if (depth === 0 && invalidImports.size > 0) { + if (depth === 0 && invalidImports.size > 0) + { res += chalk.red('Invalid imports found.\n'); - invalidImports.forEach((value, key) => { + invalidImports.forEach((value,key) => { // res += '- '+chalk.blueBright(key.split('/').pop()) + ':\n'; value.forEach((i) => { - res += chalk.red(key.split('/').pop()+" imports from '" + i + "'\n"); - if(i.indexOf('../../') === 0) + res += chalk.red(key.split('/').pop() + ' imports from \'' + i + '\'\n'); + if (i.indexOf('../../') === 0) { res += - "To fix: import from the NPM package directly.\n"; + 'To fix: import from the NPM package directly.\n'; } - else if('/src/') { - res += "To fix: you likely need to replace /src with /lib\n"; + else if ('/src/') + { + res += 'To fix: you likely need to replace /src with /lib\n'; } }); }); - - throw res; // process.exit(1); - } else if (depth === 0 && invalidImports.size === 0) { + } + else if (depth === 0 && invalidImports.size === 0) + { // console.info('All imports OK'); // process.exit(0); return true; @@ -1096,7 +1240,7 @@ export const checkImports = async ( export const depCheckStaged = async () => { console.log('Checking dependencies of staged files'); - stagedGitFiles(async function (err, results) { + stagedGitFiles(async function(err,results) { const packages = new Set(); await Promise.all( results.map(async (file) => { @@ -1107,8 +1251,8 @@ export const depCheckStaged = async () => { ); [...packages].forEach((packageRoot) => { - const pack = JSON.parse(fs.readFileSync(packageRoot, 'utf8')); - const srcPath = packageRoot.replace('package.json', ''); + const pack = JSON.parse(fs.readFileSync(packageRoot,'utf8')); + const srcPath = packageRoot.replace('package.json',''); console.log('Checking dependencies of ' + chalk.blue(pack.name) + ':'); return depCheck(process.cwd() + '/' + srcPath); // console.log('check dependencies of ' + pack.name); @@ -1140,7 +1284,7 @@ export const depCheck = async (packagePath: string = process.cwd()) => { if (missingLincdPackages.length > 0) { reject(chalk.red( - packagePath.split("/").pop() + + packagePath.split('/').pop() + '\n[ERROR] These LINCD packages are imported but they are not listed in package.json:\n- ' + missingLincdPackages.join(',\n- '), )); @@ -1148,11 +1292,13 @@ export const depCheck = async (packagePath: string = process.cwd()) => { else if (missing.length > 0) { resolve(chalk.redBright( - 'warning: '+packagePath.split("/").pop() + + 'warning: ' + packagePath.split('/').pop() + ' is missing dependencies:\n - ' + missing.join('\n - '), )); - } else { + } + else + { resolve(true); } } @@ -1169,11 +1315,13 @@ export const depCheck = async (packagePath: string = process.cwd()) => { }); }; export const ensureEnvironmentLoaded = async () => { - if (!process.env.NODE_ENV) { + if (!process.env.NODE_ENV) + { //load env-cmd for development environment - let {GetEnvVars} = await import('env-cmd'); - let envCmdrcPath = path.join(process.cwd(), '.env-cmdrc.json'); - if (!fs.existsSync(envCmdrcPath)) { + let { GetEnvVars } = await import('env-cmd'); + let envCmdrcPath = path.join(process.cwd(),'.env-cmdrc.json'); + if (!fs.existsSync(envCmdrcPath)) + { console.warn( 'No .env-cmdrc.json found in this folder. Are you running this command from the root of a LINCD app?', ); @@ -1187,30 +1335,37 @@ export const ensureEnvironmentLoaded = async () => { let environments = Object.keys(vars); //if _main is present, load it first - if (environments.includes('_main')) { - process.env = {...process.env, ...vars._main}; + if (environments.includes('_main')) + { + process.env = { ...process.env,...vars._main }; } //if --env is passed, load that environment let args = process.argv.splice(2); - if (args.includes('--env')) { + if (args.includes('--env')) + { let envIndex = args.indexOf('--env'); let env = args[envIndex + 1]; env.split(',').forEach((singleEnvironment) => { - if (environments.includes(singleEnvironment)) { + if (environments.includes(singleEnvironment)) + { console.log('Environment: ' + singleEnvironment); - process.env = {...process.env, ...vars[singleEnvironment]}; - } else { + process.env = { ...process.env,...vars[singleEnvironment] }; + } + else + { console.warn( 'Environment ' + - singleEnvironment + - ' not found in .env-cmdrc.json. Available environments: ' + - environments.join(', '), + singleEnvironment + + ' not found in .env-cmdrc.json. Available environments: ' + + environments.join(', '), ); } }); - } else { + } + else + { //chose development by default - process.env = {...process.env, ...vars.development}; + process.env = { ...process.env,...vars.development }; console.log('No environment specified, using development'); } } @@ -1221,24 +1376,39 @@ export const startServer = async ( ) => { await ensureEnvironmentLoaded(); - let lincdConfig = await import(path.join(process.cwd(), 'lincd.config.js')); + let lincdConfig = await import(path.join(process.cwd(),'lincd.config.js')); - if (!ServerClass) { - ServerClass = (await import('lincd-server/lib/shapes/LincdServer.js')).LincdServer; + // function scssLoadcall(source, filename) { + // return 'console.log("SCSS CALL: ' + filename + '");\n' + source; + // process.exit(); + // } + // hook.hook('.scss', scssLoadcall); + // hook.hook('.css', scssLoadcall); + // import.meta. + // // hook.hook('*.css', scssLoadcall); + // // hook.hook('Body.module.css', scssLoadcall); + // hook.hook('.module.css', scssLoadcall); + + if (!ServerClass) + { + ServerClass = (await import('lincd-server/shapes/LincdServer')).LincdServer; } - await import(path.join(process.cwd(), 'scripts', 'storage-config.js')); + await import(path.join(process.cwd(),'scripts','storage-config.js')); let server = new ServerClass({ loadAppComponent: async () => - (await import(path.join(process.cwd(), 'src', 'App'))).default, + (await import(path.join(process.cwd(),'src','App'))).default, ...lincdConfig, }); //Important to use slice, because when using clusers, child processes need to be able to read the same arguments let args = process.argv.slice(2); //if --initOnly is passed, only initialize the server and don't start it - if (args.includes('--initOnly') || initOnly) { + if (args.includes('--initOnly') || initOnly) + { return server.initOnly(); - } else { + } + else + { return server.start(); } }; @@ -1247,19 +1417,23 @@ export const buildApp = async () => { const webpackAppConfig = await (await import('./config-webpack-app.js')).getWebpackAppConfig(); console.log(chalk.magenta(`Building ${process.env.NODE_ENV} app bundles`)); - return new Promise((resolve, reject) => { - webpack(webpackAppConfig as any, async (err, stats) => { - if (err) { + return new Promise((resolve,reject) => { + webpack(webpackAppConfig as any,async (err,stats) => { + if (err) + { console.error(err.stack || err); process.exit(1); } const info = stats.toJson(); - if (stats.hasErrors()) { + if (stats.hasErrors()) + { console.log('Finished running webpack with errors.'); info.errors.forEach((e) => console.error(e)); // process.exit(1); reject(); - } else { + } + else + { console.log( stats.toString({ chunks: false, @@ -1284,27 +1458,31 @@ export const buildApp = async () => { }); }).then(async () => { // make sure environment is not development for storage config - if (process.env.NODE_ENV === 'development') { + if (process.env.NODE_ENV === 'development') + { console.warn('Upload build to storage skip in development environment'); process.exit(); } - if(process.env.APP_ENV) { + if (process.env.APP_ENV) + { console.warn('Not uploading to CDN for app builds'); process.exit(); } // load the storage config const storageConfig = await import( - path.join(process.cwd(), 'scripts', 'storage-config.js') - ); + path.join(process.cwd(),'scripts','storage-config.js') + ); // check if LincdFileStorage has a default FileStore // if yes: copy all the files in the build folder over with LincdFileStorage - if (LinkedFileStorage.getDefaultStore()) { + if (LinkedFileStorage.getDefaultStore()) + { // get public directory const rootDirectory = 'public'; - const pathDir = path.join(process.cwd(), rootDirectory); - if (!fs.existsSync(pathDir)) { + const pathDir = path.join(process.cwd(),rootDirectory); + if (!fs.existsSync(pathDir)) + { console.warn( 'No public directory found. Please create a public directory in the root of your project', ); @@ -1319,10 +1497,10 @@ export const buildApp = async () => { // replace pathDir with rootDirectory in filePath to get pathname // example: /Users/username/project/www/index.html -> /project/www/index.html - const pathname = filePath.replace(pathDir, `/${rootDirectory}`); + const pathname = filePath.replace(pathDir,`/${rootDirectory}`); // upload file to storage - return await LinkedFileStorage.saveFile(pathname, fileContent); + return await LinkedFileStorage.saveFile(pathname,fileContent); }); const urls = await Promise.all(uploads); @@ -1334,52 +1512,144 @@ export const buildApp = async () => { export const upgradePackages = async () => { await ensureEnvironmentLoaded(); - let packages = getLincdPackages(); - packages = packages.filter((pkg) => { - pkg.packageName !== 'lincd' + // let packages = getLincdPackages(); + // let packages = getLocalLincdModules(); + let packages = getLocalLincdPackageMap(); + let dirname = getScriptDir(); + const tsConfigCJS = path.join(dirname,'../../defaults/package','tsconfig-cjs.json'); + const tsConfigESM = path.join(dirname,'../../defaults/package','tsconfig-esm.json'); + const typesFile = path.join(dirname,'../../defaults/package/src','types.d.ts'); + + const tsConfigTemplate = await fs.readJson(path.join(dirname,'../../defaults/package','tsconfig.json')).catch(err => { + console.log(err); }); - packages.forEach(pkg => { - console.log('Upgrading ' + pkg.packageName); - // execPromise(`cd ${pkg.path} && yarn upgrade`).then(() => { - // console.log('Upgraded ' + pkg.packageName); - // }).catch(err => { - // console.warn(err); - // }) - }) + runOnPackagesGroupedByDependencies(packages,(packageGroup,dependencies) => { + // packageGroup.forEach((pkg) => { + // console.log(' Upgrading ' + pkg.packageName); + console.log('-----'); + return async (pkg: PackageDetails) => { + if (pkg.packageName === 'lincd') return; + // await execPromise(`cd ${pkg.path} && yarn upgrade`); + console.log('Upgrading ' + pkg.packageName); + // + // //create a new file src/tsconfig-cjs.json + // //copy the contents of tsconfig.json into it + // if (!fs.existsSync(path.join(pkg.path,'tsconfig-cjs.json'))) + // { + // await fs.copy(tsConfigCJS,path.join(pkg.path,'tsconfig-cjs.json')); + // await fs.copy(tsConfigESM,path.join(pkg.path,'tsconfig-esm.json')); + // console.log('Copied new tsconfig to ' + pkg.packageName); + // } + // + // //read tsconfig + // await fs.readJson(path.join(pkg.path,'tsconfig.json')).then((tsconfig) => { + // let oldCompilerOpts = tsconfig.compilerOptions; + // tsconfig.compilerOptions = tsConfigTemplate.compilerOptions; + // tsconfig.compilerOptions.types = oldCompilerOpts.types; + // tsconfig.compilerOptions.plugins = [{"name": "typescript-plugin-css-modules"}]; + // + // console.log('Upgraded tsconfig for ' + pkg.packageName); + // return fs.writeJson(path.join(pkg.path,'tsconfig.json'),tsconfig,{spaces: 2}); + // }); + // //import types at the beginning of index.ts + // addLineToIndex(`import './types';`,null,pkg.path,true); + // //copy over the types file + // await fs.copy(typesFile,path.join(pkg.path,'src','types.d.ts')); + + await fs.readJson(path.join(pkg.path,'package.json')).then((packageJson) => { + let version = packageJson.version; + let nextVersion; + if (version.split('.').shift() === '0') + { + nextVersion = getNextMajorVersion(version); + } + else + { + nextVersion = getNextMinorVersion(version); + } + console.log('Upgraded version for ' + pkg.packageName + ' to ' + nextVersion); + + packageJson.version = nextVersion; + packageJson.devDependencies['tsconfig-to-dual-package'] = '^1.2.0'; + packageJson.devDependencies['typescript-plugin-css-modules'] = '^5.1.0'; + + packageJson.main = 'lib/cjs/index.js'; + packageJson.module = 'lib/esm/index.js'; + packageJson.exports = { + '.': { + 'types': './lib/esm/index.d.ts', + 'import': './lib/esm/index.js', + 'require': './lib/cjs/index.js', + }, + './*': { + 'types': './lib/esm/*.d.ts', + 'import': './lib/esm/*.js', + 'require': './lib/cjs/*.js', + }, + }; + packageJson.typesVersions = { + '*': { + '*': [ + 'lib/esm/*', + ], + }, + }; + + return fs.writeJson(path.join(pkg.path,'package.json'),packageJson,{ spaces: 2 }); + }); -} + }; + // }); + },() => { + console.log('Finished upgrading packages'); + }); + + // packages.forEach((pkg,key) => { + // console.log(key+' Upgrading ' + pkg.packageName); + // execPromise(`cd ${pkg.path} && yarn upgrade`).then(() => { + // console.log('Upgraded ' + pkg.packageName); + // }).catch(err => { + // console.warn(err); + // }) + // }); + +}; export const createPackage = async ( name, uriBase?, basePath = process.cwd(), ) => { - if (!name) { + if (!name) + { console.warn('Please provide a name as the first argument'); return; } //if ran with npx, basePath will be the root directory of the repository, even if we're executing from a sub folder (the root directory is where node_modules lives and package.json with workspaces) //so we manually find a packages folder, if it exists we go into that. - if (fs.existsSync(path.join(basePath, 'packages'))) { - basePath = path.join(basePath, 'packages'); + if (fs.existsSync(path.join(basePath,'packages'))) + { + basePath = path.join(basePath,'packages'); } //for lincd.org currently packages are stored in the modules folder - else if (fs.existsSync(path.join(basePath, 'modules'))) { - basePath = path.join(basePath, 'modules'); + else if (fs.existsSync(path.join(basePath,'modules'))) + { + basePath = path.join(basePath,'modules'); } //let's remove scope for variable names - let [packageName, scope, cleanPackageName] = name.match( + let [packageName,scope,cleanPackageName] = name.match( /(@[\w\-]+\/)?([\w\-]+)/, ); - let targetFolder = ensureFolderExists(basePath, cleanPackageName); + let targetFolder = ensureFolderExists(basePath,cleanPackageName); - if (!uriBase) { + if (!uriBase) + { uriBase = 'http://lincd.org/ont/' + name; } - setVariable('uri_base', uriBase + '/'); + setVariable('uri_base',uriBase + '/'); //find @scope and the next part between 2 slashes after //so @dacore/some-mod/lib/file.js @@ -1388,16 +1658,16 @@ export const createPackage = async ( // --> match[2] = some-mod //but save full scoped package name under ${package_name} - setVariable('package_name', name); + setVariable('package_name',name); //extra variable for clarity (will be same as 'name') - setVariable('output_file_name', name); + setVariable('output_file_name',name); - let {hyphenName, camelCaseName, underscoreName} = + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(cleanPackageName); - log("Creating new LINCD package '" + name + "'"); - fs.copySync(path.join(__dirname, '..', 'defaults', 'package'), targetFolder); + log('Creating new LINCD package \'' + name + '\''); + fs.copySync(path.join(__dirname,'..','defaults','package'),targetFolder); //replace variables in some of the copied files await Promise.all( @@ -1409,7 +1679,7 @@ export const createPackage = async ( 'src/ontologies/example-ontology.ts', 'src/data/example-ontology.json', ] - .map((f) => path.join(targetFolder, f)) + .map((f) => path.join(targetFolder,f)) .map((file) => { return replaceVariablesInFile(file); }), @@ -1423,16 +1693,16 @@ export const createPackage = async ( ].forEach((f) => { let parts = f.split('/'); let newParts = [...parts]; - let [name, ...extensions] = newParts.pop().split('.'); + let [name,...extensions] = newParts.pop().split('.'); let newName = hyphenName + '.' + extensions.join('.'); console.log( 'rename ', - path.join(targetFolder, f), - path.join(targetFolder, ...newParts, newName), + path.join(targetFolder,f), + path.join(targetFolder,...newParts,newName), ); fs.renameSync( - path.join(targetFolder, f), - path.join(targetFolder, ...newParts, newName), + path.join(targetFolder,f), + path.join(targetFolder,...newParts,newName), ); }); @@ -1459,19 +1729,19 @@ export const createPackage = async ( ); }; -var getNextVersion = function (version) { +var getNextVersion = function(version) { let parts = version.split('.'); return parts[0] + '.' + parts[1] + '.' + (parseInt(parts[2]) + 1).toString(); }; -var getNextMajorVersion = function (version) { +var getNextMajorVersion = function(version) { let parts = version.split('.'); return (parseInt(parts[0]) + 1).toString() + '.0.0'; }; -var getNextMinorVersion = function (version) { +var getNextMinorVersion = function(version) { let parts = version.split('.'); return parts[0] + '.' + (parseInt(parts[1]) + 1).toString() + '.0'; }; -var buildFailed = function (output: string) { +var buildFailed = function(output: string) { return ( output.indexOf('Aborted due to warnings') !== -1 && output.indexOf('Command failed') !== -1 @@ -1515,10 +1785,11 @@ var buildFailed = function (output: string) { } });*/ -export const register = function (registryURL) { - if (fs.existsSync(process.cwd() + '/package.json')) { +export const register = function(registryURL) { + if (fs.existsSync(process.cwd() + '/package.json')) + { var pack = JSON.parse( - fs.readFileSync(process.cwd() + '/package.json', 'utf8'), + fs.readFileSync(process.cwd() + '/package.json','utf8'), ); let version = pack.version; let packageName = pack.name; @@ -1533,29 +1804,33 @@ export const register = function (registryURL) { console.log( chalk.cyan( 'registering package ' + - packageName + - ' ' + - version + - ' in the LINCD registry', + packageName + + ' ' + + version + + ' in the LINCD registry', ), ); - return fetch(registryURL + '/register', { + return fetch(registryURL + '/register',{ method: 'POST', headers: { Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/json', }, - body: JSON.stringify({package: packageName, version}), + body: JSON.stringify({ package: packageName,version }), // body: JSON.stringify({package: packageName, version, author}), }) .then((res) => res.json()) .then((json) => { - if (json.error) { + if (json.error) + { console.log(chalk.red('Response: ' + json.error)); - } else if (json.result) { + } + else if (json.result) + { console.log(chalk.blueBright('Response: ') + json.result); - if (json.warning) { + if (json.warning) + { console.log(chalk.red('Warning: ') + json.warning); } } @@ -1565,7 +1840,9 @@ export const register = function (registryURL) { chalk.red('Warning: ') + 'Could not connect to LINCD registry', ); }); - } else { + } + else + { console.warn( chalk.red('Warning:') + ' not found: ' + process.cwd() + '/package.json', ); @@ -1579,29 +1856,46 @@ export const buildPackage = async ( logResults: boolean = true, ) => { - const spinner = ora({ - discardStdin: true, - text: 'Compiling code', - }).start(); - let buildProcess:Promise = Promise.resolve(true); + let spinner; + if (logResults) + { + //TODO: replace with listr so we can show multiple processes at once + spinner = ora({ + discardStdin: true, + text: 'Compiling code', + }).start(); + } + let buildProcess: Promise = Promise.resolve(true); let buildStep = (step) => { buildProcess = buildProcess.then((previousResult) => { - spinner.text = step.name; - spinner.start(); + if (logResults) + { + spinner.text = step.name; + spinner.start(); + } return step.apply().then(stepResult => { - if(typeof stepResult === 'string') { + if (typeof stepResult === 'string') + { // spinner.text = step.name + ' - ' + stepResult; - spinner.warn(step.name + ' - ' + stepResult) - spinner.stop(); + if (logResults) + { + spinner.warn(step.name + ' - ' + stepResult); + spinner.stop(); + } //warning is shown, but build is still succesful with warnings return false; - } else if(stepResult === true || typeof stepResult === 'undefined') { - spinner.succeed(); + } + else if (stepResult === true || typeof stepResult === 'undefined') + { + if (logResults) + { + spinner.succeed(); + } return previousResult && true; } - }) + }); }); - } + }; buildStep({ name: 'Compiling code', @@ -1612,73 +1906,95 @@ export const buildPackage = async ( let compileCJS = `yarn exec tsc -p tsconfig-cjs.json`; let compileESM = `yarn exec tsc -p tsconfig-esm.json`; let compileCommand; - if(cjsConfig && esmConfig) { + if (cjsConfig && esmConfig) + { compileCommand = `${compileCJS} && ${compileESM}`; - } else if(cjsConfig) { + } + else if (cjsConfig) + { compileCommand = compileCJS; - } else if(esmConfig) { + } + else if (esmConfig) + { compileCommand = compileESM; - } else { + } + else + { compileCommand = `yarn exec tsc`; } - return execPromise(compileCommand).then(res => { + return execPromise(compileCommand,false,false,{ cwd: packagePath }).then(res => { return res === ''; - }) - } + }); + }, }); buildStep({ - name: 'Copying files', + name: 'Copying files to lib folder', apply: async () => { - return Promise.all( - [new Promise((resolve,reject) => { - copyfiles(['src/**/*.json', 'src/**/*.d.ts', 'src/**/*.scss', 'src/**/*.css','lib/esm'],1,(err) => { - if(err) { - reject(err); - } - else - { - resolve(true); - } - }) - }), - new Promise((resolve,reject) => { - copyfiles(['src/**/*.json','src/**/*.d.ts','src/**/*.scss','src/**/*.css','lib/cjs'],1,(err) => { - if (err) - { - return reject(err); - } - resolve(true); - }) - }) - ]).then((results) => { - return results.every(r => r === true) + const files = await glob(packagePath+'/src/**/*.{json,d.ts,css,scss}'); + return Promise.all(files.map((async (file) => { + try { + await fs.copy(file,packagePath+'/lib/esm/'+file.replace(packagePath+'/src/','')); + await fs.copy(file,packagePath+'/lib/cjs/'+file.replace(packagePath+'/src/','')); + return true; + } catch(err) { + console.warn(err); + return false + }; + }))).then((allResults) => { + return allResults.every(r => r === true); }); - } + }, }); buildStep({ - name: 'Checking dependencies', - apply: depCheck + name: 'Dual package support', + apply: () => { + return execPromise('yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json',false,false,{ cwd: packagePath }).then(res => { + return res === ''; + }); + }, + }); + buildStep({ + name:'Removing old files from lib folder', + apply: async () => { + return removeOldFiles(packagePath); + } }); buildStep({ name: 'Checking imports', - apply: checkImports + apply: () => checkImports(packagePath), + }); + buildStep({ + name: 'Checking dependencies', + apply: () => depCheck(packagePath), }); let success = await buildProcess.catch(err => { - let msg = (err && err.stdout && err.error) ? (chalk.red('error')+err.error + ':\n'+err.stdout) : err.toString(); - spinner.stopAndPersist({ - symbol: chalk.red('✖'), - text: 'Build failed', - }); + //err.error + ':\n' + + let msg = (err && err.stdout && err.error) ? err.stdout : err.toString(); + if (logResults) + { + spinner.stopAndPersist({ + symbol: chalk.red('✖'), + text: 'Build failed', + }); + } + else + { + console.log(chalk.red(packagePath.split('/').pop(),' - Build failed:')); + } console.log(msg); }); - //will be undefined if there was an error - if(typeof success !== 'undefined') + //will be undefined if there was an error + if (typeof success !== 'undefined') { - spinner.stopAndPersist({ - symbol: chalk.greenBright('✔'), - text: success === true ? 'Build successful' : 'Build successful with warnings', - }); + if(logResults) + { + spinner.stopAndPersist({ + symbol: chalk.greenBright('✔'), + text: success === true ? 'Build successful' : 'Build successful with warnings', + }); + } + } return success; // 'build-lib': 'yarn exec tsc --pretty', @@ -1695,14 +2011,13 @@ export const buildPackage = async ( // filter: 'isFile', // }, - - // command: 'yarn lincd depcheck', //'check-imports': 'yarn lincd check-imports', - - if (target == 'production' || target == 'es5' || target == 'es6' || !target) { - if (!fs.existsSync(path.join(packagePath, 'Gruntfile.js'))) { + /*if (target == 'production' || target == 'es5' || target == 'es6' || !target) + { + if (!fs.existsSync(path.join(packagePath,'Gruntfile.js'))) + { console.warn( `No Gruntfile found at ${packagePath}\\Gruntfile.js. Cannot build.`, ); @@ -1710,31 +2025,37 @@ export const buildPackage = async ( } var nodeEnv = ''; - if (target == 'production') { + if (target == 'production') + { if ( !(target2 == 'es5' || target2 == 'es6' || typeof target2 == 'undefined') - ) { - console.warn('unknown second build target. Use es5 or es6', target2); + ) + { + console.warn('unknown second build target. Use es5 or es6',target2); return; } var isWindows = /^win/.test(process.platform); - if (isWindows) { + if (isWindows) + { nodeEnv = 'SET NODE_ENV=production&& '; - } else { - nodeEnv = "NODE_ENV='production' "; + } + else + { + nodeEnv = 'NODE_ENV=\'production\' '; } } - if (!target) { + if (!target) + { target = 'es6'; } log( 'building once: ' + - nodeEnv + - 'grunt build' + - (target ? '-' + target : '') + - (target2 ? '-' + target2 : '') + - ' --color', + nodeEnv + + 'grunt build' + + (target ? '-' + target : '') + + (target2 ? '-' + target2 : '') + + ' --color', ); let method = logResults ? execp : execPromise; @@ -1750,22 +2071,24 @@ export const buildPackage = async ( //execute the command to build the method, and provide the current work directory as option return method( nodeEnv + - 'grunt build' + - (target ? '-' + target : '') + - (target2 ? '-' + target2 : '') + - ' --color', + 'grunt build' + + (target ? '-' + target : '') + + (target2 ? '-' + target2 : '') + + ' --color', false, false, - {cwd: packagePath}, + { cwd: packagePath }, ).catch((err) => { process.exit(1); }); - } else { - console.warn('unknown build target. Use es5, es6 or production.'); } + else + { + console.warn('unknown build target. Use es5, es6 or production.'); + }*/ }; -export var publishUpdated = function (test: boolean = false) { +export var publishUpdated = function(test: boolean = false) { let packages = getLocalLincdModules(); var p: Promise = Promise.resolve(''); @@ -1788,7 +2111,8 @@ export var publishUpdated = function (test: boolean = false) { let shouldPublish; var pack = getPackageJSON(pckg.path); let version = getNextVersion(pack.version); - if (pack.private) { + if (pack.private) + { shouldPublish = false; debugInfo(chalk.blue('--> is private, skipping')); @@ -1800,8 +2124,10 @@ export var publishUpdated = function (test: boolean = false) { .then(async (output: string) => { console.log('testing npm done'); var info; - try { - if (output == '' || output.includes('E404')) { + try + { + if (output == '' || output.includes('E404')) + { debugInfo( 'Empty or 404 response from `npm info`. This package was probably not published before', ); @@ -1810,11 +2136,14 @@ export var publishUpdated = function (test: boolean = false) { shouldPublish = true; //don't patch the version number (default, see above), use the current version version = pack.version; - } else { + } + else + { info = JSON.parse(output); } - if (info) { + if (info) + { let lastPublish; //yarn: // let lastPublish = info.data.time[info.data.version]; @@ -1829,14 +2158,17 @@ export var publishUpdated = function (test: boolean = false) { let lastPublishDate = new Date(lastPublish); // let {lastModifiedTime, lastModifiedName, lastModified} = getLastModifiedSourceTime(pkg.path); let lastCommitInfo = await getLastCommitTime(pckg.path); - if (!lastCommitInfo) { + if (!lastCommitInfo) + { shouldPublish = false; debugInfo('Could not determine last git commit'); // return previousResult + ' ' + chalk.red(pckg.packageName + ' - could not determine last commit\n'); return chalk.red( pckg.packageName + ' - could not determine last commit', ); - } else { + } + else + { //NOTE: removed lastModified, because switching branches will say that the file was modified and cause everything to publish //SO: now you NEED TO commit before it picks up that you should publish shouldPublish = @@ -1853,32 +2185,35 @@ export var publishUpdated = function (test: boolean = false) { shouldPublish && lastCommitInfo.changes.includes('package.json') && numberOfFilesChanges === 1 - ) { + ) + { shouldPublish = false; } - if (shouldPublish) { + if (shouldPublish) + { log( chalk.magenta(pckg.packageName) + - ' should be published because:', + ' should be published because:', ); log( lastPublishDate.toDateString() + - ' ' + - lastPublishDate.toTimeString() + - ' published ' + - info.version, + ' ' + + lastPublishDate.toTimeString() + + ' published ' + + info.version, ); log( lastCommitInfo.date.toDateString() + - ' ' + - new Date(lastCommitInfo.date).toTimeString() + - ' source last committed:', + ' ' + + new Date(lastCommitInfo.date).toTimeString() + + ' source last committed:', ); log(lastCommitInfo.changes); } } } - } catch (err) { + } catch (err) + { // var stats = fs.statSync(path.join(packageDirectory)); // var files = fs.readdirSync(path.join(packageDirectory,'src')); console.log( @@ -1888,21 +2223,25 @@ export var publishUpdated = function (test: boolean = false) { // return previousResult + ' ' + chalk.red(pckg.packageName + ' failed: ' + err.message + '\n'); return chalk.red(pckg.packageName + ' failed: ' + err.message); } - if (shouldPublish) { - return publishPackage(pckg, test, info, version); + if (shouldPublish) + { + return publishPackage(pckg,test,info,version); } return ( chalk.blue(pckg.packageName) + ' latest version is up to date' ); }) - .catch(({error, stdout, stderr}) => { - if (error) { + .catch(({ error,stdout,stderr }) => { + if (error) + { console.log(error.message); } - if (stdout) { + if (stdout) + { console.log(stderr); } - if (stderr) { + if (stderr) + { console.log(stderr); } // return previousResult + ' ' + chalk.red(pckg.packageName + ' failed\n'); @@ -1937,19 +2276,23 @@ export var publishUpdated = function (test: boolean = false) { }); }; -async function getEnvJsonPath(relativeToPath = process.cwd()) { +async function getEnvJsonPath(relativeToPath = process.cwd()) +{ let path = ''; - if (!relativeToPath.endsWith('/')) { + if (!relativeToPath.endsWith('/')) + { relativeToPath += '/'; } // let path = './'; - for (let i = 0; i <= 10; i++) { + for (let i = 0; i <= 10; i++) + { let envFile = await getEnvFile({ filePath: relativeToPath + path + '.env.json', }).catch((err) => { return null; }); - if (envFile) { + if (envFile) + { //note: we're getting the actual contents here, so we could also use that more directly? return path + '.env.json'; } @@ -1957,23 +2300,26 @@ async function getEnvJsonPath(relativeToPath = process.cwd()) { } } -export var publishPackage = async function ( +export var publishPackage = async function( pkg?, test?, info?, publishVersion?, ) { - if (!pkg) { + if (!pkg) + { let localPackageJson = getPackageJSON(); pkg = { path: process.cwd(), packageName: localPackageJson.name, }; } - if (!publishVersion) { + if (!publishVersion) + { publishVersion = info ? getNextVersion(info.version) : 'patch'; } - if (test) { + if (test) + { debugInfo('should publish ' + pkg.packageName + ' ' + publishVersion); //when testing what needs to be published return chalk.blue(pkg.packageName + ' should publish'); @@ -2000,17 +2346,18 @@ export var publishPackage = async function ( if ( res.indexOf('Aborted due to warnings') !== -1 || res.indexOf('Could not publish') !== -1 || - res.indexOf("Couldn't publish") !== -1 - ) { + res.indexOf('Couldn\'t publish') !== -1 + ) + { console.log(res); return chalk.red(pkg.packageName + ' failed\n'); } console.log( 'Successfully published ' + - chalk.green(pkg.path) + - ' ' + - chalk.magenta(publishVersion), + chalk.green(pkg.path) + + ' ' + + chalk.magenta(publishVersion), ); return ( chalk.green(pkg.packageName) + @@ -2018,13 +2365,13 @@ export var publishPackage = async function ( chalk.magenta(publishVersion) ); }) - .catch(({error, stdout, stderr}) => { + .catch(({ error,stdout,stderr }) => { console.log(chalk.red('Failed to publish: ' + error.message)); return chalk.red(pkg.packageName + ' failed to publish'); }); }; -export var buildUpdated = async function ( +export var buildUpdated = async function( back, target, target2, @@ -2052,7 +2399,8 @@ export var buildUpdated = async function ( //if either cli or jsonldPkg needs to be rebuilt // if (jsonldPkgUpdated || cliPkgUpdated) { - if (jsonldPkgUpdated) { + if (jsonldPkgUpdated) + { await execPromise( 'yarn exec tsc && echo "compiled lincd-jsonld"', false, @@ -2073,7 +2421,7 @@ export var buildUpdated = async function ( let packagesLeft = packages.size; runOnPackagesGroupedByDependencies( packages, - (packageGroup, dependencies) => { + (packageGroup,dependencies) => { debugInfo( 'Now checking: ' + chalk.blue(packageGroup.map((i) => i.packageName)), ); @@ -2088,31 +2436,42 @@ export var buildUpdated = async function ( // true, ); - if (pkg.packageName === 'lincd-jsonld' && jsonldPkgUpdated) { + if (pkg.packageName === 'lincd-jsonld' && jsonldPkgUpdated) + { needRebuild = true; } - if (needRebuild || rebuildAllModules) { + if (needRebuild || rebuildAllModules) + { //TODO: when building a pkg, also rebuild all packages that depend on this package.. and iteratively build packages that depend on those packages.. // log(packageName+' modified since last commit on '+now.toString()); - if (test) { + if (test) + { debugInfo('Need to build ' + pkg.packageName); return chalk.blue(pkg.packageName + ' should be build'); } log('Building ' + pkg.packageName); + // return buildPackage(null,null,pkg.path) return execPromise( 'cd ' + - pkg.path + - ' && yarn build' + - (target ? ' ' + target : '') + - (target2 ? ' ' + target2 : ''), + pkg.path + + ' && yarn build' + + (target ? ' ' + target : '') + + (target2 ? ' ' + target2 : ''), ) .then((res) => { - debugInfo(chalk.green(pkg.packageName + ' successfully built')); - return chalk.green(pkg.packageName + ' built'); + if(res === '') + { + debugInfo(chalk.green(pkg.packageName + ' successfully built')); + return chalk.green(pkg.packageName + ' built'); + } else if (typeof res === 'string') { + warn(chalk.red('Failed to build ' + pkg.packageName)); + console.log(res); + process.exit(1); + } }) - .catch(({error, stdout, stderr}) => { + .catch(({ error,stdout,stderr }) => { warn(chalk.red('Failed to build ' + pkg.packageName)); console.log(stdout); process.exit(1); @@ -2131,10 +2490,13 @@ export var buildUpdated = async function ( }; }, (results) => { - if (results.length) { + if (results.length) + { log('Summary:'); log(results.join('\n')); - } else { + } + else + { log(chalk.green('Nothing to rebuild')); } }, @@ -2143,25 +2505,30 @@ export var buildUpdated = async function ( return; }; -const printBuildResults = function (failed, done) { - log( - 'Successfully built: ' + +const printBuildResults = function(failed,done) { + if (done.length > 0) + { + log( + 'Successfully built: ' + chalk.green([...done].map((m) => m.packageName).join(', ')) + '\n', - ); - if (failed.length > 0) { + ); + } + if (failed.length > 0) + { warn('Failed to build: ' + chalk.red(failed.join(', ')) + '\n'); } }; -export var executeCommandForEachPackage = function ( +export var executeCommandForEachPackage = function( packages, command, filterMethod, filterValue, ) { //if a specific set of packages is given - if (filterMethod == 'exclude') { + if (filterMethod == 'exclude') + { //filter packages, so that we only execute on the packages as provided in the command log('Excluding ' + filterValue); filterValue = filterValue.split(','); @@ -2173,9 +2540,11 @@ export var executeCommandForEachPackage = function ( //by default start executing, unless 'from' is given let executing: boolean = true; //option to start from a specific pkg in the stack - if (filterMethod == 'from') { + if (filterMethod == 'from') + { startFrom = filterValue; - if (startFrom) { + if (startFrom) + { console.log(chalk.blue('Will skip ahead to ' + startFrom)); } let seen = false; @@ -2183,7 +2552,8 @@ export var executeCommandForEachPackage = function ( if ( !seen && (pkg.packageName == startFrom || pkg.packageName == startFrom) - ) { + ) + { seen = true; } return seen; @@ -2191,10 +2561,10 @@ export var executeCommandForEachPackage = function ( } log( - "Executing '" + - chalk.blueBright(command) + - "' on packages " + - chalk.magenta(packages.map((m) => m.packageName).join(', ')), + 'Executing \'' + + chalk.blueBright(command) + + '\' on packages ' + + chalk.magenta(packages.map((m) => m.packageName).join(', ')), ); var p = Promise.resolve(true); @@ -2207,34 +2577,34 @@ export var executeCommandForEachPackage = function ( return p; }; -var gitIgnore = function (...entries) { +var gitIgnore = function(...entries) { //add each entry to the .gitignore file - let gitIgnorePath = path.resolve(process.cwd(), '.gitignore'); - addLinesToFile(gitIgnorePath, entries); + let gitIgnorePath = path.resolve(process.cwd(),'.gitignore'); + addLinesToFile(gitIgnorePath,entries); }; -export var addLinesToFile = function (filePath, entries) { - let fileContents = fs.readFileSync(filePath, {encoding: 'utf8'}); +export var addLinesToFile = function(filePath,entries) { + let fileContents = fs.readFileSync(filePath,{ encoding: 'utf8' }); entries.forEach((entry) => { fileContents += '\n' + entry; }); - fs.writeFileSync(filePath, fileContents); + fs.writeFileSync(filePath,fileContents); }; -export var addCapacitor = async function (basePath = process.cwd()) { +export var addCapacitor = async function(basePath = process.cwd()) { let targetFolder = ensureFolderExists(basePath); log('Adding capacitor'); fs.copySync( - path.join(__dirname, '..', 'defaults', 'app-static'), + path.join(__dirname,'..','defaults','app-static'), targetFolder, ); fs.copySync( - path.join(__dirname, '..', 'defaults', 'capacitor', 'scripts'), - path.join(targetFolder, 'scripts'), + path.join(__dirname,'..','defaults','capacitor','scripts'), + path.join(targetFolder,'scripts'), ); //update .env-cmdrc.json file - let envCmdPath = path.resolve(basePath, '.env-cmdrc.json'); - let envCmd = JSON.parse(fs.readFileSync(envCmdPath, {encoding: 'utf8'})); + let envCmdPath = path.resolve(basePath,'.env-cmdrc.json'); + let envCmd = JSON.parse(fs.readFileSync(envCmdPath,{ encoding: 'utf8' })); envCmd['app-main'] = { APP_ENV: true, @@ -2251,7 +2621,7 @@ export var addCapacitor = async function (basePath = process.cwd()) { SITE_ROOT: 'http://localhost:4000', }; - fs.writeFile(envCmdPath, JSON.stringify(envCmd, null, 2)); + fs.writeFile(envCmdPath,JSON.stringify(envCmd,null,2)); log('Edited .env-cmdrc.json'); gitIgnore( @@ -2276,12 +2646,12 @@ export var addCapacitor = async function (basePath = process.cwd()) { pack.scripts['cap:sync'] = 'yarn cap sync'; fs.writeFile( - path.resolve(basePath, 'package.json'), - JSON.stringify(pack, null, 2), + path.resolve(basePath,'package.json'), + JSON.stringify(pack,null,2), ); log('Added new run script to package.json'); - await execPromise(`yarn add -D @capacitor/cli`, true, false, null, true); + await execPromise(`yarn add -D @capacitor/cli`,true,false,null,true); await execPromise( `yarn add @capacitor/android @capacitor/core @capacitor/app @capacitor/ios`, false, @@ -2315,31 +2685,70 @@ export var addCapacitor = async function (basePath = process.cwd()) { ); }; -export var executeCommandForPackage = function (packageName, command) { +export var executeCommandForPackage = function(packageName,command) { let packageDetails = getLincdPackages().find( (modDetails: PackageDetails) => modDetails.packageName.indexOf(packageName) !== -1 || modDetails.packageName.indexOf(packageName) !== -1, ); - if (packageDetails) { + if (packageDetails) + { log( - "Executing 'cd " + - packageDetails.path + - ' && yarn exec lincd' + - (command ? ' ' + command : '') + - "'", + 'Executing \'cd ' + + packageDetails.path + + ' && yarn exec lincd' + + (command ? ' ' + command : '') + + '\'', ); return execp( 'cd ' + - packageDetails.path + - ' && yarn exec lincd' + - (command ? ' ' + command : ''), + packageDetails.path + + ' && yarn exec lincd' + + (command ? ' ' + command : ''), ); - } else { + } + else + { warn( - "Could not find a pkg who's name (partially) matched " + - chalk.cyan(packageName), + 'Could not find a pkg who\'s name (partially) matched ' + + chalk.cyan(packageName), ); } }; + +/** + * Function to remove files older than 10 seconds from the 'lib' folder. + * @param {string} packagePath - The path to the package directory. + */ +export const removeOldFiles = async (packagePath) => { + const libPath = path.join(packagePath, 'lib'); + + try { + // Read all files in the 'lib' folder asynchronously + const files = await glob(packagePath + '/lib/**/*.*') + + // Iterate through each file + for (const file of files) { + // const filePath = path.join(libPath, file); + + // Check if the file exists before attempting to delete it + // if (await fs.pathExists(filePath)) { + const stats = await fs.stat(file); + const currentTime = new Date().getTime(); + const lastModifiedTime = stats.mtime.getTime(); + + // Check if the difference between the current time and last modified time is greater than 10 seconds + if (currentTime - lastModifiedTime > 10000) { + // Attempt to delete the file + await fs.unlink(file); + // console.log(`Removed: ${file}`); + } + // } + } + return true; + } catch (error) { + console.error(`Error removing files: ${error.message}`); + return false + } +}; \ No newline at end of file diff --git a/src/cli.ts b/src/cli.ts index 8517ac2..bdd17bb 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -23,7 +23,7 @@ import { developPackage, executeCommandForEachPackage, executeCommandForPackage, - getLincdPackages, + getLincdPackages,getScriptDir, publishPackage, publishUpdated, register, @@ -161,7 +161,7 @@ program program .command('info') .action(() => { - const localDir = dirname(import.meta.url).replace('file:/', ''); + let localDir = getScriptDir(); let packageJsonPath =path.join(localDir, 'package.json') try { var ownPackage = JSON.parse( diff --git a/src/loaders/css-loader.mts b/src/loaders/css-loader.mts new file mode 100644 index 0000000..ad9d713 --- /dev/null +++ b/src/loaders/css-loader.mts @@ -0,0 +1,75 @@ +import createLoader from 'create-esm-loader' +import parseCSS from 'css-parse'; +import { generateScopedName } from '../utils.js'; + +const cssLoader = { + resolve(specifier, opts) { + if (specifier.endsWith('.css') || specifier.endsWith('.scss')) { + let { parentURL } = opts; + let url = new URL(specifier, parentURL).href; + return { url }; + } + }, + format(url, opts) { + if (url.endsWith('.css') || url.endsWith('.scss')) { + return { format: 'module' }; + } + }, + transform(source, opts) { + const { url } = opts + if (url.endsWith('.css') || url.endsWith('.scss')) { + + // const { outputText } = ts.transpileModule(String(source), { + // compilerOptions: { + // module: ts.ModuleKind.ES2020, + // }, + // }) + let cssClassesObject = parseCssToObject(String(source),opts.url); + let finalSource = JSON.stringify(cssClassesObject,null,2); + return { source: `export default ${finalSource};`}; + } + }, +}; + +function parseCssToObject(rawSource:string,filename) { + const output = {}; + //TODO: replace with parse scss + let myResults = rawSource.match(/\.[a-zA-Z_]{1}[\w]+[\s:]/g); + if(myResults) { + myResults.map(result => { + return result.replace(/[\.\s:]/g,'') + }).forEach(selector => { + let scopedClassName = generateScopedName(process.env.NODE_ENV === 'production',false,selector,filename,null); + output[selector] = scopedClassName; + }) + } + // console.log(myResults); + // for (const rule of parseCSS(rawSource).stylesheet.rules) { + // if(rule.selectors) + // { + // let selector = rule['selectors'].at(-1); // Get right-most in the selector rule: `.Bar` in `.Foo > .Bar {…}` + // if (selector[0] !== '.') break; // only care about classes + // + // selector = selector + // .substring(1) // Skip the initial `.` + // .match(/(\w+)/)[1]; // Get only the classname: `Qux` in `.Qux[type="number"]` + // + // output[selector] = selector;//getClassStyles(rule['declarations']); + // // : selector; + // } + // } + + return output; +} + +function getClassStyles(declarations) { + const styles = {}; + + for (const declaration of declarations) { + styles[declaration['property']] = declaration['value']; + } + + return styles; +} +//@ts-ignore +export const { resolve, load } = await createLoader(cssLoader); \ No newline at end of file diff --git a/src/loaders/register.ts b/src/loaders/register.ts new file mode 100644 index 0000000..763c65d --- /dev/null +++ b/src/loaders/register.ts @@ -0,0 +1,3 @@ +import { register } from 'node:module'; +//@ts-ignore +register('./css-loader.mjs', import.meta.url); \ No newline at end of file diff --git a/src/metadata.ts b/src/metadata.ts index 08eb07e..6f5b751 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -1,154 +1,154 @@ -import {NamedNode} from 'lincd/models'; -import {GetEnvVars} from 'env-cmd'; -import path from 'path'; -import fs from 'fs-extra'; -import {createNameSpace} from 'lincd/utils/NameSpace'; -import {Prefix} from 'lincd/utils/Prefix'; -import {getPackageJSON} from './utils'; -import {warn} from './cli-methods'; -import {JSONLDWriter} from 'lincd-jsonld/lib/utils/JSONLDWriter'; - -function getLocalPackagePaths() { - let packagePaths = []; - - //add the APP package itself - let appPackagePath = process.cwd(); - let appLincdPackagePath = path.join( - appPackagePath, - 'frontend', - 'src', - 'package.ts', - ); - if (fs.existsSync(appPackagePath)) { - let packageJSON = getPackageJSON(appPackagePath); - - packagePaths.push([ - packageJSON.name, - appPackagePath, - appLincdPackagePath, - true, - ]); - } else { - console.log('Not a LINCD app'); - } - - //NOTE: we could also switch to checking 'workspaces'? - let packagesFolder = path.join(process.cwd(), 'packages'); - if (fs.existsSync(packagesFolder)) { - let localPackages = fs.readdirSync(packagesFolder); - localPackages.forEach((packageFolderName) => { - packagePaths.push([ - packageFolderName, - path.join(packagesFolder, packageFolderName), - path.join(packagesFolder, packageFolderName, 'lib', 'package.js'), - ]); - }); - } - return packagePaths; -} - -export const buildMetadata = async (): Promise => { - // require('@babel/register')({extensions: ['js', '.ts', '.tsx']}); - - //NOTE: we can not rely on the LincdWebApp shape from lincd-server here, because that would make the initial build of all modules a lot trickier - //see, CLI needs to be built as one of the first things in order to build other things. So it cannot rely on lincd-server, which relies on 10 other packages - let app: NamedNode; - - //set the URL of the app as the URI of its node - let envVars: any = await GetEnvVars({ - envFile: { - filePath: '.env-cmdrc.json', - }, - }); - if ( - envVars[process.env.NODE_ENV] && - envVars[process.env.NODE_ENV].SITE_ROOT - ) { - //get the site root of the current environment - app = NamedNode.getOrCreate(envVars[process.env.NODE_ENV].SITE_ROOT); - } else { - warn( - 'Cannot find environment variable SITE_ROOT. Make sure SITE_ROOT is set (likely in .env-cmdrc.json) for the current environment: ' + - process.env.NODE_ENV, - ); - app = NamedNode.create(); - } - - let updatedPaths = []; - var localPackagePaths = getLocalPackagePaths(); - - //prepare output path - let metadataFolder = path.join(process.cwd(), 'data', 'metadata'); - await fs.ensureDir(metadataFolder); //{recursive:true} but not needed with fs-extra - - for (const [ - packageCodeName, - packagePath, - lincdPackagePath, - isAppPackage, - ] of localPackagePaths) { - let errors = false; - //TODO: check if this resolves, if not, skip it (for initial setup) - import('lincd-modules/lib/scripts/package-metadata.js').then( - async (script) => { - await script - .getPackageMetadata(packagePath, lincdPackagePath) - .then(async (response) => { - if (response.errors.length > 0) { - // console.log(JSON.stringify(response)); - warn( - 'Error processing ' + - packagePath + - ':\n' + - response.errors.join('\n'), - ); - // throw response - errors = true; - } else { - if (!response.packageUri) { - console.warn( - 'No package URI from meta data. Not building meta data for this package', - ); - return; - } - let pkgNode = NamedNode.getOrCreate(response.packageUri); - //connect the packages to the app - let lincdApp = createNameSpace('http://lincd.org/ont/lincd-app/'); - Prefix.add('lincdApp', 'http://lincd.org/ont/lincd-app/'); - if (isAppPackage) { - //Note: this needs to match with LincdWebApp.ownPackage accessor; - app.overwrite(lincdApp('ownPackage'), pkgNode); - } else { - //Note: this needs to match with LincdWebApp.packages accessor; - app.set(lincdApp('maintainsPackage'), pkgNode); - } - - //write this graph to a jsonld file - let packageMetaData = JSON.stringify(response.result, null, 2); - let metadataFile = path.join( - metadataFolder, - packageCodeName + '.json', - ); - await fs.writeFile(metadataFile, packageMetaData).then(() => { - updatedPaths.push(metadataFile); - }); - } - }); - }, - ); - - //enable this when testing if you don't want to continue with building other metadata when an errors occur - // if (errors) break; - } - - let packageMetaData = await JSONLDWriter.stringify( - app as any, - process.env.NODE_ENV === 'development', - ); - let metadataFile = path.join(metadataFolder, '_app.json'); - await fs.writeFile(metadataFile, packageMetaData).then(() => { - updatedPaths.push(metadataFile); - }); - - return updatedPaths; -}; +// import {NamedNode} from 'lincd/models'; +// import {GetEnvVars} from 'env-cmd'; +// import path from 'path'; +// import fs from 'fs-extra'; +// import {createNameSpace} from 'lincd/utils/NameSpace'; +// import {Prefix} from 'lincd/utils/Prefix'; +// import {getPackageJSON} from './utils'; +// import {warn} from './cli-methods'; +// import {JSONLDWriter} from 'lincd-jsonld/utils/JSONLDWriter'; +// +// function getLocalPackagePaths() { +// let packagePaths = []; +// +// //add the APP package itself +// let appPackagePath = process.cwd(); +// let appLincdPackagePath = path.join( +// appPackagePath, +// 'frontend', +// 'src', +// 'package.ts', +// ); +// if (fs.existsSync(appPackagePath)) { +// let packageJSON = getPackageJSON(appPackagePath); +// +// packagePaths.push([ +// packageJSON.name, +// appPackagePath, +// appLincdPackagePath, +// true, +// ]); +// } else { +// console.log('Not a LINCD app'); +// } +// +// //NOTE: we could also switch to checking 'workspaces'? +// let packagesFolder = path.join(process.cwd(), 'packages'); +// if (fs.existsSync(packagesFolder)) { +// let localPackages = fs.readdirSync(packagesFolder); +// localPackages.forEach((packageFolderName) => { +// packagePaths.push([ +// packageFolderName, +// path.join(packagesFolder, packageFolderName), +// path.join(packagesFolder, packageFolderName, 'lib', 'package.js'), +// ]); +// }); +// } +// return packagePaths; +// } +// +// export const buildMetadata = async (): Promise => { +// // require('@babel/register')({extensions: ['js', '.ts', '.tsx']}); +// +// //NOTE: we can not rely on the LincdWebApp shape from lincd-server here, because that would make the initial build of all modules a lot trickier +// //see, CLI needs to be built as one of the first things in order to build other things. So it cannot rely on lincd-server, which relies on 10 other packages +// let app: NamedNode; +// +// //set the URL of the app as the URI of its node +// let envVars: any = await GetEnvVars({ +// envFile: { +// filePath: '.env-cmdrc.json', +// }, +// }); +// if ( +// envVars[process.env.NODE_ENV] && +// envVars[process.env.NODE_ENV].SITE_ROOT +// ) { +// //get the site root of the current environment +// app = NamedNode.getOrCreate(envVars[process.env.NODE_ENV].SITE_ROOT); +// } else { +// warn( +// 'Cannot find environment variable SITE_ROOT. Make sure SITE_ROOT is set (likely in .env-cmdrc.json) for the current environment: ' + +// process.env.NODE_ENV, +// ); +// app = NamedNode.create(); +// } +// +// let updatedPaths = []; +// var localPackagePaths = getLocalPackagePaths(); +// +// //prepare output path +// let metadataFolder = path.join(process.cwd(), 'data', 'metadata'); +// await fs.ensureDir(metadataFolder); //{recursive:true} but not needed with fs-extra +// +// for (const [ +// packageCodeName, +// packagePath, +// lincdPackagePath, +// isAppPackage, +// ] of localPackagePaths) { +// let errors = false; +// //TODO: check if this resolves, if not, skip it (for initial setup) +// import('lincd-modules/scripts/package-metadata.js').then( +// async (script) => { +// await script +// .getPackageMetadata(packagePath, lincdPackagePath) +// .then(async (response) => { +// if (response.errors.length > 0) { +// // console.log(JSON.stringify(response)); +// warn( +// 'Error processing ' + +// packagePath + +// ':\n' + +// response.errors.join('\n'), +// ); +// // throw response +// errors = true; +// } else { +// if (!response.packageUri) { +// console.warn( +// 'No package URI from meta data. Not building meta data for this package', +// ); +// return; +// } +// let pkgNode = NamedNode.getOrCreate(response.packageUri); +// //connect the packages to the app +// let lincdApp = createNameSpace('http://lincd.org/ont/lincd-app/'); +// Prefix.add('lincdApp', 'http://lincd.org/ont/lincd-app/'); +// if (isAppPackage) { +// //Note: this needs to match with LincdWebApp.ownPackage accessor; +// app.overwrite(lincdApp('ownPackage'), pkgNode); +// } else { +// //Note: this needs to match with LincdWebApp.packages accessor; +// app.set(lincdApp('maintainsPackage'), pkgNode); +// } +// +// //write this graph to a jsonld file +// let packageMetaData = JSON.stringify(response.result, null, 2); +// let metadataFile = path.join( +// metadataFolder, +// packageCodeName + '.json', +// ); +// await fs.writeFile(metadataFile, packageMetaData).then(() => { +// updatedPaths.push(metadataFile); +// }); +// } +// }); +// }, +// ); +// +// //enable this when testing if you don't want to continue with building other metadata when an errors occur +// // if (errors) break; +// } +// +// let packageMetaData = await JSONLDWriter.stringify( +// app as any, +// process.env.NODE_ENV === 'development', +// ); +// let metadataFile = path.join(metadataFolder, '_app.json'); +// await fs.writeFile(metadataFile, packageMetaData).then(() => { +// updatedPaths.push(metadataFile); +// }); +// +// return updatedPaths; +// }; diff --git a/src/utils.ts b/src/utils.ts index afd2fd5..3df95c8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import chalk from 'chalk'; -import {exec} from 'child_process'; +import { exec,ExecOptions } from 'child_process'; import ts from 'typescript'; import {builtinModules} from 'module'; import {PackageDetails} from 'interfaces'; @@ -197,11 +197,12 @@ export var getLINCDDependencies = function ( // a simple sort with dependencyMap doesn't seem to work,so we start with LINCD (least dependencies) and from there add packages that have all their dependencies already added let sortedPackagePaths = []; let addedPackages = new Set(['lincd']); - sortedPackagePaths.push( - lincdPackagePaths.find(([packageName]) => { - return packageName === 'lincd'; - }), - ); + let lincdItself = lincdPackagePaths.find(([packageName]) => { + return packageName === 'lincd'; + }); + if(lincdItself) { + sortedPackagePaths.push(lincdItself); + } while (addedPackages.size !== lincdPackagePaths.length) { let startSize = addedPackages.size; @@ -407,7 +408,7 @@ export function execPromise( command, log = false, allowError: boolean = false, - options?: any, + options?: ExecOptions, pipeOutput: boolean = false, ): Promise { return new Promise(function (resolve, reject) { @@ -457,8 +458,9 @@ export function generateScopedName( .substring(0, 6); return hash; } - var filename = path.basename(filepath, '.scss'); - let nearestPackageJson = findNearestPackageJsonSync(filename); + var filename = path.basename(filepath).replace(/\.(module\.)?(css|scss)/,''); + let resolved = path.resolve(filepath).replace(/[\w\-_\/]+\/file\:/,''); + let nearestPackageJson = findNearestPackageJsonSync(resolved); let packageName = nearestPackageJson ? nearestPackageJson.data.name : 'unknown'; diff --git a/tsconfig.json b/tsconfig.json index 28a67cb..f7021c8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,6 @@ "module": "esnext", "types": ["node"] }, - "include": ["./src/**/*.ts","./src/**/*.cts"], + "include": ["./src/**/*.ts","./src/**/*.cts","./src/**/*.mts"], "files": ["./src/index.ts", "./src/cli.ts"] } \ No newline at end of file From b62709c543242657b81a280ae06da196fd361862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Sun, 12 May 2024 12:35:40 +0200 Subject: [PATCH 08/87] only 1 place now where CSS scoped names are generated. updated config-webpack-app as its now used by LincdServer for SSR & hot module reloading in development mode, as well as `yarn build` to build production bundles. --- package.json | 4 + src/cli-methods.ts | 126 ---------------------- src/config-webpack-app.ts | 213 ++++++++++++++++--------------------- src/loaders/css-loader.mts | 11 +- src/utils.ts | 30 +++--- 5 files changed, 121 insertions(+), 263 deletions(-) diff --git a/package.json b/package.json index 7c66685..719860d 100644 --- a/package.json +++ b/package.json @@ -112,10 +112,13 @@ "ora": "^8.0.1", "postcss": "^8.4.35", "postcss-comment": "^2.0.0", + "postcss-font-magician": "^4.0.0", "postcss-import": "^16.0.1", "postcss-loader": "^8.1.1", "postcss-modules": "^6.0.0", "postcss-nested": "^6.0.1", + "postcss-preset-env": "^9.5.0", + "postcss-reporter": "^7.1.0", "postcss-scss": "^4.0.9", "postcss-strip-inline-comments": "^0.1.5", "react-refresh-typescript": "^2.0.9", @@ -132,6 +135,7 @@ "tsconfig-to-dual-package": "^1.2.0", "tsx": "^4.7.2", "typescript": "^4.8.4", + "typescript-plugin-css-modules": "^5.1.0", "webpack": "^5.90.3", "webpack-bundle-analyzer": "^4.10.1", "webpack-license-plugin": "^4.4.2", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 06ce927..6581eaf 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -1747,43 +1747,6 @@ var buildFailed = function(output: string) { output.indexOf('Command failed') !== -1 ); }; -/*program.command('shapes').action(async () => { - //we've imported require-extensions from npm so that we can use this - //we want to avoid nodejs tripping up over @import commands in css files - require.extensions['.scss'] = function (sourcecode, filename) { - return {}; - }; - require.extensions['.css'] = function (sourcecode, filename) { - return {}; - }; - - if (fs.existsSync(process.cwd() + '/package.json')) { - var pack = JSON.parse( - fs.readFileSync(process.cwd() + '/package.json', 'utf8'), - ); - let packageName = pack.name; - - //just making sure the library is loaded in correct order because circular references are currently happening when importing BlankNode before NamedNode for example - // require('lincd'); - //TODO: replace with actual index file from package.json, or tsconfig - let indexExports = require(process.cwd() + '/lib/index.js'); - if(indexExports.packageExports) - { - let shapeJSONLD = await getShapesJSONLD(indexExports.packageExports); - console.log(indexExports.packageExports); - console.log(shapeJSONLD); - console.log(chalk.bold(chalk.green(packageName+'/dist/shapes.json'))); - return fs.writeFile(path.join('dist', 'shapes.json'), shapeJSONLD); - } - else - { - console.warn("Invalid LINCD package. Index file should export a packageExports object. See examples.") - } - - } else { - console.warn('Not a project'); - } -});*/ export const register = function(registryURL) { if (fs.existsSync(process.cwd() + '/package.json')) @@ -1997,95 +1960,6 @@ export const buildPackage = async ( } return success; - // 'build-lib': 'yarn exec tsc --pretty', - - // 'copy:lib', - // var copyfiles = require('copyfiles'); - // copyfiles([paths], opt, callback); - - // { - // expand: true, - // src: ['**/*.json', '**/*.d.ts', '**/*.scss', '**/*.css'], - // dest: config.outputPath || 'lib/', - // cwd: 'src/', - // filter: 'isFile', - // }, - - // command: 'yarn lincd depcheck', - //'check-imports': 'yarn lincd check-imports', - - /*if (target == 'production' || target == 'es5' || target == 'es6' || !target) - { - if (!fs.existsSync(path.join(packagePath,'Gruntfile.js'))) - { - console.warn( - `No Gruntfile found at ${packagePath}\\Gruntfile.js. Cannot build.`, - ); - return; - } - - var nodeEnv = ''; - if (target == 'production') - { - if ( - !(target2 == 'es5' || target2 == 'es6' || typeof target2 == 'undefined') - ) - { - console.warn('unknown second build target. Use es5 or es6',target2); - return; - } - var isWindows = /^win/.test(process.platform); - if (isWindows) - { - nodeEnv = 'SET NODE_ENV=production&& '; - } - else - { - nodeEnv = 'NODE_ENV=\'production\' '; - } - } - if (!target) - { - target = 'es6'; - } - - log( - 'building once: ' + - nodeEnv + - 'grunt build' + - (target ? '-' + target : '') + - (target2 ? '-' + target2 : '') + - ' --color', - ); - let method = logResults ? execp : execPromise; - - //NOTE: we moved SCSS:JSON out of webpack and grunt, into this file - //this is the beginning of a transition away from grunt - //but for the time being it's perhaps a bit strange that we - // let x = postcss([ - // postcssModules({ - // generateScopedName, - // }), - // ]); - - //execute the command to build the method, and provide the current work directory as option - return method( - nodeEnv + - 'grunt build' + - (target ? '-' + target : '') + - (target2 ? '-' + target2 : '') + - ' --color', - false, - false, - { cwd: packagePath }, - ).catch((err) => { - process.exit(1); - }); - } - else - { - console.warn('unknown build target. Use es5, es6 or production.'); - }*/ }; export var publishUpdated = function(test: boolean = false) { diff --git a/src/config-webpack-app.ts b/src/config-webpack-app.ts index 8b9612b..1354ae8 100644 --- a/src/config-webpack-app.ts +++ b/src/config-webpack-app.ts @@ -5,140 +5,105 @@ import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import webpack from 'webpack'; import fs from 'fs'; -import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer'; +import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; import TerserPlugin from 'terser-webpack-plugin'; -import {findNearestPackageJsonSync} from 'find-nearest-package-json'; -import {getLINCDDependencies, getLinkedTailwindColors} from './utils.js'; +import { + generateScopedName, + generateScopedNameProduction, + getLINCDDependencies, + getLinkedTailwindColors, +} from './utils.js'; import tailwindPlugin from 'tailwindcss/plugin.js'; -import {LinkedFileStorage} from 'lincd/utils/LinkedFileStorage'; +import { LinkedFileStorage } from 'lincd/utils/LinkedFileStorage'; import postcssUrl from 'postcss-url'; const isProduction = process.env.NODE_ENV === 'production'; const isDevelopment = process.env.NODE_ENV === 'development'; const packageJson = JSON.parse( - fs.readFileSync(path.resolve(process.cwd(), 'package.json'), 'utf-8'), + fs.readFileSync(path.resolve(process.cwd(),'package.json'),'utf-8'), ); -// get from the project's backend config file -import(path.join(process.cwd(), 'scripts', 'storage-config.js')); -const accessURL = LinkedFileStorage.accessURL; +const cssModes = ['scss-modules','tailwind','scss','mixed']; -// Should relate to the use of express.static() in LincdServer.tsx, which makes the build files available through a URL -const publicPath = '/public'; -const bundlesPath = publicPath + '/bundles/'; -// ASSET_PATH mostly used for the apps to load the assets from the correct path -const ASSET_PATH = - process.env.ASSET_PATH || accessURL ? accessURL + bundlesPath : bundlesPath; - -const lincdConfigPathJs = path.resolve(process.cwd(), 'lincd.config.js'); -const lincdConfigPathJson = path.resolve(process.cwd(), 'lincd.config.json'); - -const cssModes = ['scss-modules', 'tailwind', 'scss', 'mixed']; - - -class RebuildScssJsonPlugin { - apply(compiler) { - compiler.hooks.watchRun.tap('beforeRun', (comp) => { - //NOTE: this code takes care of recompiling .scss.json files whenever a .scss file is changed - // we had to ignore .scss.json files to prevent infinite loops (since its input and output) - // currently this works, but the build process is one step behind - // so when devs change a .scss file, only the SECOND time that they change - // and save that file will the first change in .scss.json pickup - if (comp.modifiedFiles) { - let scssFiles = [...comp.modifiedFiles].filter((file) => { - return path.extname(file) === '.scss'; - }); - if (scssFiles.length) { - scssFiles.forEach((file) => { - // Make an entry for each file path that should recompile - compiler.inputFileSystem.purge(file + '.json'); - compiler.fileTimestamps.set(file + '.json', Date.now()); - compiler.inputFileSystem.purge(file); - compiler.fileTimestamps.set(file, Date.now()); - // console.log(chalk.magenta('Purged:'), file+'.json'); - }); - // Triggers the recompile, but somehow only after the current emit is finished - compiler.watching.invalidate(() => { - // console.log('Recompile finished'); - }); - - return; - } - // if([...comp.modifiedFiles].every(file => { - // return path.extname(file) === '' - // })) { - // // prevent rebuild? - // compiler.modifiedFiles = new Set(); - // compiler.finish((a,b) => { - // console.log("Prevented build?",a,b); - // }); - // // compiler.stop(() => { - // // console.log("Prevented build?"); - // // }); - // return; - // } - } - }); - } -} -class WatchRunPlugin { - apply(compiler) { - compiler.hooks.watchRun.tap('watchRun', (comp) => { - if (comp.modifiedFiles) { +class WatchRunPlugin +{ + apply(compiler) + { + compiler.hooks.watchRun.tap('watchRun',(comp) => { + if (comp.modifiedFiles) + { const changedFiles = Array.from( comp.modifiedFiles, (file) => `\n ${file}`, ).join(''); - if (changedFiles.length) { - console.log(chalk.magenta('Changed files:'), changedFiles); + if (changedFiles.length) + { + console.log(chalk.magenta('Changed files:'),changedFiles); } } }); } } -function generateScopedName(name, filename, css) { - var file = path.basename(filename, '.scss'); - let nearestPackageJson = findNearestPackageJsonSync(filename); - let packageName = nearestPackageJson - ? nearestPackageJson.data.name - : packageJson.name; - return packageName.replace(/[^a-zA-Z0-9_]+/g, '_') + '_' + file + '_' + name; +function getLocalIdent(context,currentFormat,name) +{ + return isProduction ? generateScopedNameProduction(name,context.resourcePath) : generateScopedName(name,context.resourcePath); } export const getLincdConfig = async () => { + + const lincdConfigPathJs = path.resolve(process.cwd(),'lincd.config.js'); + const lincdConfigPathJson = path.resolve(process.cwd(),'lincd.config.json'); + //default config let config: any = { //scss-modules is default cssMode: cssModes[0], analyse: false, }; -//overwriting config from package.json.lincdApp or lincd.config.js(on) file - if (typeof packageJson.lincdApp === 'object') { + //overwriting config from package.json.lincdApp or lincd.config.js(on) file + if (typeof packageJson.lincdApp === 'object') + { //overwrite default with anything that's defined in lincdApp in package.json - config = {...config, ...packageJson.lincdApp}; - } else if (fs.existsSync(lincdConfigPathJs)) { + config = { ...config,...packageJson.lincdApp }; + } + else if (fs.existsSync(lincdConfigPathJs)) + { let lincdConfig = await import(lincdConfigPathJs); - config = {...config, ...lincdConfig}; - } else if (fs.existsSync(lincdConfigPathJson)) { - let lincdConfig = JSON.parse(fs.readFileSync(lincdConfigPathJson, 'utf-8')) - config = {...config, ...lincdConfig}; + config = { ...config,...lincdConfig }; } - return config; -} - -export const getWebpackAppConfig = async () => { - - let config = await getLincdConfig(); - - if (!cssModes.includes(config.cssMode)) { + else if (fs.existsSync(lincdConfigPathJson)) + { + let lincdConfig = JSON.parse(fs.readFileSync(lincdConfigPathJson,'utf-8')); + config = { ...config,...lincdConfig }; + } + if (!cssModes.includes(config.cssMode)) + { console.warn( 'Invalid value for property cssMode. Should be one of: ' + cssModes.join(', '), ); process.exit(); } + return config; +}; + +export const getWebpackAppConfig = async () => { + + // get from the project's config-frontend file + await import(path.join(process.cwd(),'scripts','storage-config.js')); + const accessURL = LinkedFileStorage.accessURL; + + // Should relate to the use of express.static() in LincdServer.tsx, which makes the build files available through a URL + const publicPath = '/public'; + const bundlesPath = publicPath + '/bundles/'; + // ASSET_PATH mostly used for the apps to load the assets from the correct path + const ASSET_PATH = + process.env.ASSET_PATH || accessURL ? accessURL + bundlesPath : bundlesPath; + + let config = await getLincdConfig(); + let postcssPlugins = [ postcssUrl({ url: (asset) => { @@ -150,7 +115,8 @@ export const getWebpackAppConfig = async () => { config.cssMode === 'scss-modules' || config.cssMode === 'scss' || config.cssMode === 'mixed' - ) { + ) + { postcssPlugins = postcssPlugins.concat([ // ['stylelint', { // 'extends': [ @@ -185,23 +151,25 @@ export const getWebpackAppConfig = async () => { isProduction && 'cssnano', // "postcss-reporter", ]); - if (config.cssMode === 'scss-modules' || config.cssMode === 'mixed') { - postcssPlugins.push([ - 'postcss-modules', - { - generateScopedName: generateScopedName, - globalModulePaths: (Array.isArray(config.cssGlobalModulePaths) - ? [/tailwind/, ...config.cssGlobalModulePaths] - : [/tailwind/, config.cssGlobalModulePaths] - ).filter(Boolean), - }, - ]); + if (config.cssMode === 'scss-modules' || config.cssMode === 'mixed') + { + // postcssPlugins.push([ + // 'postcss-modules', + // { + // generateScopedName: generateScopedName, + // globalModulePaths: (Array.isArray(config.cssGlobalModulePaths) + // ? [/tailwind/, ...config.cssGlobalModulePaths] + // : [/tailwind/, config.cssGlobalModulePaths] + // ).filter(Boolean), + // }, + // ]); } } - if (config.cssMode === 'tailwind' || config.cssMode === 'mixed') { + if (config.cssMode === 'tailwind' || config.cssMode === 'mixed') + { //make sure that tailwind classes from any LINCD packages that are listed in package.json:dependencies are included let lincdPackagePaths: any = getLINCDDependencies(packageJson); - lincdPackagePaths = lincdPackagePaths.map(([packageName, packagePath]) => { + lincdPackagePaths = lincdPackagePaths.map(([packageName,packagePath]) => { return packagePath + '/lib/**/*.{js,mjs}'; }); // console.log( @@ -219,7 +187,7 @@ export const getWebpackAppConfig = async () => { : { //in development mode we allow all classes here, so that you can easily develop pattern: /./, - variants: ['sm', 'md', 'lg', 'xl', '2xl'], + variants: ['sm','md','lg','xl','2xl'], }, theme: { extend: { @@ -227,7 +195,7 @@ export const getWebpackAppConfig = async () => { }, }, plugins: [ - tailwindPlugin(function ({addBase, config}) { + tailwindPlugin(function({ addBase,config }) { //we can use LINCD CSS variables for default font color, size etc. // addBase({ // 'h1': { fontSize: config('theme.fontSize.2xl') }, @@ -274,7 +242,7 @@ export const getWebpackAppConfig = async () => { }, plugins: [ // new WatchRunPlugin(), - new RebuildScssJsonPlugin(), + // new RebuildScssJsonPlugin(), new MiniCssExtractPlugin(), new webpack.EnvironmentPlugin(Object.keys(process.env)), // new ForkTsCheckerWebpackPlugin(), @@ -294,12 +262,16 @@ export const getWebpackAppConfig = async () => { loader: 'css-loader', options: { url: false, - // modules:{ - // exportOnlyLocals:true, - // getLocalIdent: this.getLocalIdent.bind(this), - // localIdentName: "[path][name]__[local]--[hash:base64:5]", - // }, importLoaders: 1, + modules: { + mode: 'local', + // namedExport:true, + // exportOnlyLocals:true, + getLocalIdent: getLocalIdent, + localIdentName: '[local]--[hash:base64:6]', + // localIdentName: "[path][name]__[local]--[hash:base64:5]", + }, + }, }, { @@ -347,6 +319,7 @@ export const getWebpackAppConfig = async () => { module: 'esnext', moduleResolution: 'node', sourceMap: isDevelopment, + plugins: [{ 'name': 'typescript-plugin-css-modules' }], }, }), options: { @@ -383,11 +356,11 @@ export const getWebpackAppConfig = async () => { modules: false, }, resolve: { - extensions: ['.tsx','.ts','.js','.scss','.scss.json','.json'], + extensions: ['.tsx','.ts','.js','.css','.scss','.json'], alias: config.alias || {}, }, //Cache is now overwritten in LincdServer based on config, the other value for type would be 'filesystem' //see also https://webpack.js.org/configuration/other-options/#cache cache: { type: 'memory' }, - } -} \ No newline at end of file + }; +}; \ No newline at end of file diff --git a/src/loaders/css-loader.mts b/src/loaders/css-loader.mts index ad9d713..db47f04 100644 --- a/src/loaders/css-loader.mts +++ b/src/loaders/css-loader.mts @@ -1,6 +1,6 @@ import createLoader from 'create-esm-loader' -import parseCSS from 'css-parse'; -import { generateScopedName } from '../utils.js'; +// import parseCSS from 'css-parse'; +import { generateScopedName,generateScopedNameProduction } from '../utils.js'; const cssLoader = { resolve(specifier, opts) { @@ -39,7 +39,12 @@ function parseCssToObject(rawSource:string,filename) { myResults.map(result => { return result.replace(/[\.\s:]/g,'') }).forEach(selector => { - let scopedClassName = generateScopedName(process.env.NODE_ENV === 'production',false,selector,filename,null); + let scopedClassName; + if(process.env.NODE_ENV === 'production') { + scopedClassName = generateScopedNameProduction(selector,filename); + } else { + scopedClassName = generateScopedName(selector,filename); + } output[selector] = scopedClassName; }) } diff --git a/src/utils.ts b/src/utils.ts index 3df95c8..b405cee 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -439,25 +439,27 @@ export function execPromise( }); } -// export function generateScopedName(moduleName,name, filename, css) { -export function generateScopedName( - isProduction, - isWebpack, +export function generateScopedNameProduction( cssClassName, filepath, - css, + css?, ) { //for app development we can use short unique hashes //but for webpack bundles of lincd modules, we need to ensure unique class names across bundles of many packages - if (isProduction && !isWebpack) { - //generate a short unique hash based on cssClassName and filepath - let hash = require('crypto') - .createHash('md5') - .update(cssClassName + filepath) - .digest('hex') - .substring(0, 6); - return hash; - } + //generate a short unique hash based on cssClassName and filepath + let hash = require('crypto') + .createHash('md5') + .update(cssClassName + filepath) + .digest('hex') + .substring(0, 6); + return hash; + +} +export function generateScopedName( + cssClassName, + filepath, + css?, +) { var filename = path.basename(filepath).replace(/\.(module\.)?(css|scss)/,''); let resolved = path.resolve(filepath).replace(/[\w\-_\/]+\/file\:/,''); let nearestPackageJson = findNearestPackageJsonSync(resolved); From bc4fa158c558a1a8425483cebc1006ff19eb9f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Thu, 23 May 2024 08:34:03 +0100 Subject: [PATCH 09/87] changes to lincd-cli on MYND updated defaults updated build command, latest glob version, added missing node-hook dependency better warning messages for runPackage build command now uses internal functions (buildPackage) instead of exec extended addLineToIndex function for adding types.d.ts to new package added getScriptDir() which functions like __dirname but works on both ESM & CJS continued upgradePackages command (and commented all out). this is good to review for turning into update documentation added compileOnly function improved functionality to copy files into lib folder by doing it more manually removing old files during build process (manually taken from main branch updates) --- defaults/app-with-backend/package.json | 10 +- defaults/package/package.json | 2 +- defaults/package/src/index.ts | 1 + .../src/ontologies/example-ontology.ts | 9 +- defaults/package/src/types.d.ts | 9 + defaults/package/tsconfig-cjs.json | 8 + defaults/package/tsconfig-esm.json | 9 + defaults/package/tsconfig.json | 15 +- package.json | 9 +- src/cli-methods.ts | 935 +++++++++++++----- src/cli.ts | 9 +- src/metadata.ts | 308 +++--- src/utils.ts | 15 +- tsconfig.json | 1 + 14 files changed, 881 insertions(+), 459 deletions(-) create mode 100644 defaults/package/src/types.d.ts create mode 100644 defaults/package/tsconfig-cjs.json create mode 100644 defaults/package/tsconfig-esm.json diff --git a/defaults/app-with-backend/package.json b/defaults/app-with-backend/package.json index 0577c79..c094fd6 100644 --- a/defaults/app-with-backend/package.json +++ b/defaults/app-with-backend/package.json @@ -59,10 +59,10 @@ "@babel/register": "^7.22.5", "chalk": "^4.1.2", "lincd": "^1.0", - "lincd-auth": "^0.1.19", - "lincd-jsonld": "^0.1.21", - "lincd-server": "^0.1.39", - "lincd-server-utils": "^0.1.7", + "lincd-auth": "~1.0", + "lincd-jsonld": "~1.0", + "lincd-server": "~1.0", + "lincd-server-utils": "~1.0", "react": "^18.2", "react-dom": "^18.2", "react-error-boundary": "^3.1.3", @@ -82,7 +82,7 @@ "eslint-plugin-react": "latest", "eslint-plugin-react-hooks": "^4.6.0", "husky": "^8.0.0", - "lincd-cli": "^1.0", + "lincd-cli": "^1.1", "nodemon": "^2.0.22", "pinst": "^3.0.0", "pm2": "^5.3.0", diff --git a/defaults/package/package.json b/defaults/package/package.json index 4eed609..9238650 100644 --- a/defaults/package/package.json +++ b/defaults/package/package.json @@ -33,6 +33,6 @@ "lincd-jsonld": "^0.1" }, "devDependencies": { - "lincd-cli": "^1.0" + "lincd-cli": "^1.1" } } diff --git a/defaults/package/src/index.ts b/defaults/package/src/index.ts index f1a7aae..0cf942a 100644 --- a/defaults/package/src/index.ts +++ b/defaults/package/src/index.ts @@ -1,3 +1,4 @@ +import './types'; import './ontologies/${hyphen_name}'; //SHAPES FIRST diff --git a/defaults/package/src/ontologies/example-ontology.ts b/defaults/package/src/ontologies/example-ontology.ts index cf6f999..87ea5b3 100644 --- a/defaults/package/src/ontologies/example-ontology.ts +++ b/defaults/package/src/ontologies/example-ontology.ts @@ -9,7 +9,14 @@ import * as _this from './${hyphen_name}'; * Load the data of this ontology into memory, thus adding the properties of the entities of this ontology to the local graph. */ export var loadData = () => { - return import('../data/${hyphen_name}.json').then((data) => JSONLD.parse(data)); + if (typeof module !== 'undefined' && typeof exports !== 'undefined') { + // CommonJS import + return import('../data/${hyphen_name}.json').then((data) => JSONLD.parse(data)); + } else { + // ESM import + //@ts-ignore + return import('../data/${hyphen_name}.json',{ assert: { type: "json" } }).then((data) => JSONLD.parse(data)); + } }; /** diff --git a/defaults/package/src/types.d.ts b/defaults/package/src/types.d.ts new file mode 100644 index 0000000..f0745b6 --- /dev/null +++ b/defaults/package/src/types.d.ts @@ -0,0 +1,9 @@ +declare module '*.module.css' { + const classes: {[key: string]: string}; + export default classes; +} + +declare module '*.module.scss' { + const classes: {[key: string]: string}; + export default classes; +} diff --git a/defaults/package/tsconfig-cjs.json b/defaults/package/tsconfig-cjs.json new file mode 100644 index 0000000..027ce41 --- /dev/null +++ b/defaults/package/tsconfig-cjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "lib/cjs" + } +} diff --git a/defaults/package/tsconfig-esm.json b/defaults/package/tsconfig-esm.json new file mode 100644 index 0000000..b668eba --- /dev/null +++ b/defaults/package/tsconfig-esm.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "esnext", + "target": "es2018", + "outDir": "lib/esm", + "moduleResolution": "node" + } +} \ No newline at end of file diff --git a/defaults/package/tsconfig.json b/defaults/package/tsconfig.json index 0b2e6e9..3b2b0d6 100644 --- a/defaults/package/tsconfig.json +++ b/defaults/package/tsconfig.json @@ -1,22 +1,15 @@ { "compilerOptions": { - "module": "commonjs", "sourceMap": true, - "target": "es6", - "moduleResolution": "node", - "outDir": "lib", "declaration": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, - "esModuleInterop": true, - "resolveJsonModule": true, "downlevelIteration": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "pretty": true, "jsx": "react", - "types": ["node", "react", "react-dom"], - "paths": { - "react": ["../node_modules/@types/react","../../../node_modules/@types/react"] - }, - "baseUrl": "./src" + "types": [] }, "files": ["./src/index.ts"], "include": ["./src/backend.ts"] diff --git a/package.json b/package.json index 967e0f0..92e1e1c 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ }, "scripts": { "prepublishOnly": "npm exec build", - "build": "rimraf ./lib && yarn build-esm && yarn dual-package", + "build": "rimraf ./lib && yarn build-esm && yarn build-cjs && yarn dual-package", "build-cjs": "echo \"compiling CJS\" && yarn tsc -p tsconfig-cjs.json", "build-esm": "echo \"compiling ESM\" && yarn tsc -p tsconfig-esm.json", "dual-package": "echo 'adding package.json to each lib folder for dual output support' && yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json", @@ -90,7 +90,7 @@ "env-cmd": "^10.1.0", "find-nearest-package-json": "^2.0.1", "fs-extra": "^11.2.0", - "glob": "^10.3.10", + "glob": "^10.3.12", "grunt": "^1.6.1", "grunt-cli": "^1.4.3", "grunt-concurrent": "^3.0.0", @@ -101,10 +101,11 @@ "grunt-webpack": "^6.0.0", "license-info-webpack-plugin": "^3.0.0", "lincd": "^1.0", - "lincd-jsonld": "^0.1.22", - "lincd-modules": "^0.1", + "lincd-jsonld": "~1.0", + "lincd-modules": "~1.0", "load-grunt-tasks": "^5.1.0", "mini-css-extract-plugin": "^2.8.1", + "node-hook": "^1.0.0", "open": "^8.4.0", "ora": "^8.0.1", "postcss": "^8.4.35", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 7bd1b06..35c08a1 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -1,10 +1,9 @@ import chalk from 'chalk'; -import {exec} from 'child_process'; +import { exec } from 'child_process'; import depcheck from 'depcheck'; -import {getEnvFile} from 'env-cmd/dist/get-env-vars.js'; +import { getEnvFile } from 'env-cmd/dist/get-env-vars.js'; import fs from 'fs-extra'; -import ts from 'typescript'; -import path from 'path'; +import path,{ dirname } from 'path'; import { debugInfo, execp, @@ -17,55 +16,55 @@ import { needsRebuilding, } from './utils.js'; -import {statSync} from 'fs'; -import {PackageDetails} from 'interfaces'; -import {findNearestPackageJson} from 'find-nearest-package-json'; -import {LinkedFileStorage} from 'lincd/utils/LinkedFileStorage'; +import { statSync } from 'fs'; +import { PackageDetails } from 'interfaces'; +import { findNearestPackageJson } from 'find-nearest-package-json'; +import { LinkedFileStorage } from 'lincd/utils/LinkedFileStorage'; // import pkg from 'lincd/utils/LinkedFileStorage'; // const { LinkedFileStorage } = pkg; - // const config = require('lincd-server/site.webpack.config'); import { glob } from 'glob'; import webpack from 'webpack'; import stagedGitFiles from 'staged-git-files'; -import copyfiles from 'copyfiles'; import ora from 'ora'; var variables = {}; -export const createApp = async (name, basePath = process.cwd()) => { - if (!name) { +export const createApp = async (name,basePath = process.cwd()) => { + if (!name) + { console.warn('Please provide a name as the first argument'); } - let {hyphenName, camelCaseName, underscoreName} = setNameVariables(name); + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(name); - let targetFolder = path.join(basePath, hyphenName); - if (!fs.existsSync(targetFolder)) { + let targetFolder = path.join(basePath,hyphenName); + if (!fs.existsSync(targetFolder)) + { fs.mkdirSync(targetFolder); } fs.copySync( - path.join(__dirname, '..', 'defaults', 'app-with-backend'), + path.join(__dirname,'..','defaults','app-with-backend'), targetFolder, ); //make sure the data folder exists (even though its empty).. copying empty folders does not work with fs.copySync - fs.mkdirSync(path.join(targetFolder, 'data'), {recursive: true}); - fs.mkdirSync(path.join(targetFolder, 'data/uploads/resized'), { + fs.mkdirSync(path.join(targetFolder,'data'),{ recursive: true }); + fs.mkdirSync(path.join(targetFolder,'data/uploads/resized'),{ recursive: true, }); fs.renameSync( - path.join(targetFolder, 'gitignore.template'), - path.join(targetFolder, '.gitignore'), + path.join(targetFolder,'gitignore.template'), + path.join(targetFolder,'.gitignore'), ); fs.renameSync( - path.join(targetFolder, 'yarnrc.yml.template'), - path.join(targetFolder, '.yarnrc.yml'), + path.join(targetFolder,'yarnrc.yml.template'), + path.join(targetFolder,'.yarnrc.yml'), ); // fs.copySync(path.join(__dirname, '..', 'defaults', 'app'), targetFolder); - log("Creating new LINCD application '" + name + "'"); + log('Creating new LINCD application \'' + name + '\''); //replace variables in some copied files await replaceVariablesInFolder(targetFolder); @@ -216,21 +215,39 @@ function runOnPackagesGroupedByDependencies( lincdPackages.has(dependency.packageName) ); }) - ) { + ) + { leastDependentPackage = pkg; } }); let startStack: PackageDetails[] = [leastDependentPackage]; - const runPackage = (runFunction, pck) => { + const runPackage = (runFunction,pck) => { return runFunction(pck) - .catch(({error, stdout, stderr}) => { - warn( - 'Uncaught exception whilst running parallel function on ' + + .catch((errorObj) => { + if (errorObj.error) + { + let { error,stdout,stderr } = errorObj; + warn( + 'Uncaught exception whilst running parallel function on ' + pck.packageName, - error.message, - ); + error?.message ? error.message : error?.toString(), + // stdout, + // stderr, + ); + } + else + { + warn( + 'Uncaught exception whilst running parallel function on ' + + pck.packageName, + errorObj?.toString(), + // stdout, + // stderr, + ); + process.exit(); + } // warn(chalk.red(pck.packageName+' failed:')); // console.log(stdout); }) @@ -465,95 +482,170 @@ export function buildAll(options) { let command; let skipping = false; //if we're skipping builds until a certain package - if (!building) { + if (!building) + { //if the package name matches the package we're supposed to start from then start building packages - if (pkg.packageName == startFrom || pkg.packageName == startFrom) { + if (pkg.packageName == startFrom || pkg.packageName == startFrom) + { building = true; } //else still waiting for the package - else { + else + { log(chalk.blue('skipping ' + pkg.packageName)); command = Promise.resolve(true); skipping = true; } } //unless told otherwise, build the package - if (!command) { - command = execPromise( - 'cd ' + pkg.path + ' && yarn exec lincd build', - // (target ? ' ' + target : '') + - // (target2 ? ' ' + target2 : ''), - false, - false, - {}, - false, - ); + if (!command) + { + command = buildPackage(null,null,path.join(process.cwd(),pkg.path),false); + // command = execPromise( + // 'cd ' + pkg.path + ' && yarn exec lincd build', + // // (target ? ' ' + target : '') + + // // (target2 ? ' ' + target2 : ''), + // false, + // false, + // {}, + // false, + // ); log(chalk.cyan('Building ' + pkg.packageName)); process.stdout.write(packagesLeft + ' packages left\r'); } - return command - .catch(({error, stdout, stderr}) => { - //this prints out the webpack output, including the build errors - warn('Failed to build ' + pkg.packageName); - console.log(stdout); + return command.then(res => { + //empty string or true is success + //false is success with warnings + //any other string is the build error text + //undefined is failure + if (res !== '' && res !== true && res !== false) { failedModules.push(pkg.packageName); - let dependentModules = getDependentPackages(dependencies, pkg); - if (dependentModules.length > 0) { - printBuildResults(failedModules, done); + let dependentModules = getDependentPackages(dependencies,pkg); + if (dependentModules.length > 0) + { + printBuildResults(failedModules,done); console.log( - chalk.magenta( - 'Stopping build process because an error occurred whilst building ' + - pkg.packageName + - ', which ' + - dependentModules.length + - ' other packages depend on.', - ), + 'Stopping build process because an error occurred whilst building ' + + pkg.packageName + + ', which ' + + dependentModules.length + + ' other packages depend on.', ); //"+dependentModules.map(d => d.packageName).join(", "))); - console.log( - chalk.cyanBright('tip ') + - 'Run ' + - chalk.green(`lincd build-all --from=${pkg.packageName}`) + - ' to build only the remaining packages', + log( + 'Run ' + + chalk.greenBright(`lincd build-all --from=${pkg.packageName}`) + + ' to build only the remaining packages', ); //"+dependentModules.map(d => d.packageName).join(", "))); process.exit(1); } - }) - .then((res) => { - if (!skipping) { - log(chalk.green('Built ' + pkg.packageName)); + } + else + { + if (!skipping) + { + log(chalk.green('Built ' + pkg.packageName) + (res === false ? chalk.redBright(' (with warnings)') : '')); } done.add(pkg); packagesLeft--; // log(chalk.magenta(packagesLeft + ' packages left')); process.stdout.write(packagesLeft + ' packages left\r'); - if (packagesLeft == 0) { - printBuildResults(failedModules, done); - if (failedModules.length > 0) { + if (packagesLeft == 0) + { + printBuildResults(failedModules,done); + if (failedModules.length > 0) + { process.exit(1); } } return res; + } + }) + .catch(({ error,stdout,stderr }) => { + warn(chalk.red('Failed to build ' + pkg.packageName)); + console.log(stdout); + process.exit(1); + // let dependentModules = getDependentP }); + //undefined result means it failed + /*if (typeof res === 'undefined') + { + // .catch(({ error,stdout,stderr }) => { + //this prints out the webpack output, including the build errors + // warn('Failed to build ' + pkg.packageName); + // console.log(stdout); + failedModules.push(pkg.packageName); + let dependentModules = getDependentPackages(dependencies,pkg); + if (dependentModules.length > 0) + { + printBuildResults(failedModules,done); + console.log( + 'Stopping build process because an error occurred whilst building ' + + pkg.packageName + + ', which ' + + dependentModules.length + + ' other packages depend on.', + ); //"+dependentModules.map(d => d.packageName).join(", "))); + log( + 'Run ' + + chalk.greenBright(`lincd build-all --from=${pkg.packageName}`) + + ' to build only the remaining packages', + ); //"+dependentModules.map(d => d.packageName).join(", "))); + process.exit(1); + } + } + else //true is successful build, false is successful but with warnings + { + //successful build + // }) + // .then((res) => { + if (!skipping) + { + log(chalk.green('Built ' + pkg.packageName)+(res === false ? chalk.redBright(' (with warnings)') : '')); + } + done.add(pkg); + + packagesLeft--; + // log(chalk.magenta(packagesLeft + ' packages left')); + process.stdout.write(packagesLeft + ' packages left\r'); + if (packagesLeft == 0) + { + printBuildResults(failedModules,done); + if (failedModules.length > 0) + { + process.exit(1); + } + } + + return res; + }*/ + // }).catch(err => { + // console.log(err); + // }) }; }, (dependencies) => { //if no more packages to build but we never started building... - if (!building) { + if (!building) + { console.log( chalk.red( 'Could not find the package to start from. Please provide a correct package name or package name to build from', ), ); - } else { + } + else + { //Detecting cyclical dependencies that caused some packages not to be build let first = true; lincdPackages.forEach((pkg) => { - if (!done.has(pkg)) { + if (!done.has(pkg)) + { let deps = dependencies.get(pkg); - if (first) { + if (first) + { console.log( chalk.red( 'CYCLICAL DEPENDENCIES? - could not build some packages because they depend on each other.', @@ -795,48 +887,63 @@ export const createOntology = async ( log(`Added an import of this file from ${chalk.magenta(indexPath)}`); } }; -const addLineToIndex = function (line, insertMatchString: string) { +const addLineToIndex = function(line,insertMatchString: string,root: string = process.cwd(),insertAtStart: boolean = false) { //import ontology in index - let indexPath = ['index.ts', 'index.tsx'] - .map((f) => path.join('src', f)) + let indexPath = ['index.ts','index.tsx'] + .map((f) => path.join(root,'src',f)) .find((indexFileName) => { return fs.existsSync(indexFileName); }); - if (indexPath) { - let indexContents = fs.readFileSync(indexPath, 'utf-8'); + if (indexPath) + { + let indexContents = fs.readFileSync(indexPath,'utf-8'); let lines = indexContents.split(/\n/g); let newContents; - for (var key in lines) { - if (lines[key].indexOf(insertMatchString) !== -1) { - //remove lines after this line and insert new line in its place + for (var key in lines) + { + //if the match string is found + if (lines[key].indexOf(insertMatchString) !== -1) + { + //add the new line after this line lines[key] += `\n${line}`; newContents = lines.join('\n'); // log("Found at "+key,lines,newContents); break; } } - if (!newContents) { - newContents = indexContents + `\n${line}`; + if (!newContents) + { + if (insertAtStart) + { + newContents = `${line}\n${indexContents}`; + } + else + { + newContents = `${indexContents}\n${line}`; + } // log("Added at end",newContents); } - fs.writeFileSync(indexPath, newContents); + fs.writeFileSync(indexPath,newContents); } return indexPath; }; -const replaceVariablesInFiles = function (...files: string[]) { +const replaceVariablesInFiles = function(...files: string[]) { return Promise.all( files.map((file) => { return replaceVariablesInFile(file); }), ); }; -const replaceVariablesInFolder = function (folder: string) { +const replaceVariablesInFolder = function(folder: string) { //get all files in folder, including files that start with a dot - (glob as any)(folder + '/**/*', {dot: true, nodir: true}, async function (err, files) { - if (err) { - console.log('Error', err); - } else { + (glob as any)(folder + '/**/*',{ dot: true,nodir: true },async function(err,files) { + if (err) + { + console.log('Error',err); + } + else + { // console.log(files); await Promise.all( files.map((file) => { @@ -884,82 +991,105 @@ const ensureFolderExists = function (...folders: string[]) { return targetFolder;*/ }; -export const setNameVariables = function (name) { - let hyphenName = name.replace(/[-_\s]+/g, '-'); +export const setNameVariables = function(name) { + let hyphenName = name.replace(/[-_\s]+/g,'-'); let camelCaseName = camelCase(name); //some-package --> someModule - let underscoreName = name.replace(/[-\s]+/g, '_'); - let plainName = name.replace(/[-\s]+/g, ''); + let underscoreName = name.replace(/[-\s]+/g,'_'); + let plainName = name.replace(/[-\s]+/g,''); //longer similar variables names should come before the shorter ones - setVariable('underscore_name', underscoreName); - setVariable('hyphen_name', hyphenName); - setVariable('camel_name', camelCaseName); - setVariable('name', name); - setVariable('plain_name', plainName); + setVariable('underscore_name',underscoreName); + setVariable('hyphen_name',hyphenName); + setVariable('camel_name',camelCaseName); + setVariable('name',name); + setVariable('plain_name',plainName); - return {hyphenName, camelCaseName, underscoreName, plainName}; + return { hyphenName,camelCaseName,underscoreName,plainName }; }; -function getSourceFolder(basePath = process.cwd()) { +function getSourceFolder(basePath = process.cwd()) +{ //LINCD App - if (fs.existsSync(path.join(basePath, 'frontend', 'src'))) { - return path.join(basePath, 'frontend', 'src'); + if (fs.existsSync(path.join(basePath,'frontend','src'))) + { + return path.join(basePath,'frontend','src'); } //LINCD package - if (fs.existsSync(path.join(basePath, 'src'))) { - return path.join(basePath, 'src'); - } else { + if (fs.existsSync(path.join(basePath,'src'))) + { + return path.join(basePath,'src'); + } + else + { console.warn('Cannot find source folder'); - return path.join(basePath, 'src'); + return path.join(basePath,'src'); } } -export const createShape = async (name, basePath = process.cwd()) => { +/** + * get __dirname for either ESM/CJS + */ +export const getScriptDir = () => { + // @ts-ignore + if (typeof __dirname !== 'undefined') + { + // @ts-ignore + return __dirname; + } + else + { + // @ts-ignore + return dirname(import.meta.url).replace('file:/',''); + } + +}; +export const createShape = async (name,basePath = process.cwd()) => { let sourceFolder = getSourceFolder(basePath); - let targetFolder = ensureFolderExists(sourceFolder, 'shapes'); - let {hyphenName, camelCaseName, underscoreName} = setNameVariables(name); + let targetFolder = ensureFolderExists(sourceFolder,'shapes'); + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(name); //copy default shape file // log("Creating files for shape '" + name + "'"); - let targetFile = path.join(targetFolder, hyphenName + '.ts'); - fs.copySync(path.join(__dirname, '..', 'defaults', 'shape.ts'), targetFile); + let targetFile = path.join(targetFolder,hyphenName + '.ts'); + fs.copySync(path.join(__dirname,'..','defaults','shape.ts'),targetFile); //replace variables in some of the copied files await replaceVariablesInFiles(targetFile); log( `Created a new shape class template in ${chalk.magenta( - targetFile.replace(basePath, ''), + targetFile.replace(basePath,''), )}`, ); //if this is NOT a lincd app (but a lincd package) let indexPath; - if (!sourceFolder.includes('frontend')) { - indexPath = addLineToIndex(`import './shapes/${hyphenName}';`, 'shapes'); + if (!sourceFolder.includes('frontend')) + { + indexPath = addLineToIndex(`import './shapes/${hyphenName}';`,'shapes'); log(`Added an import of this file from ${chalk.magenta(indexPath)}`); } }; -export const createSetComponent = async (name, basePath = process.cwd()) => { - let targetFolder = ensureFolderExists(basePath, 'src', 'components'); - let {hyphenName, camelCaseName, underscoreName} = setNameVariables(name); +export const createSetComponent = async (name,basePath = process.cwd()) => { + let targetFolder = ensureFolderExists(basePath,'src','components'); + let { hyphenName,camelCaseName,underscoreName } = setNameVariables(name); //copy default shape file - log("Creating files for set component '" + name + "'"); - let targetFile = path.join(targetFolder, hyphenName + '.tsx'); + log('Creating files for set component \'' + name + '\''); + let targetFile = path.join(targetFolder,hyphenName + '.tsx'); fs.copySync( - path.join(__dirname, '..', 'defaults', 'set-component.tsx'), + path.join(__dirname,'..','defaults','set-component.tsx'), targetFile, ); - let targetFile2 = path.join(targetFolder, hyphenName + '.scss'); + let targetFile2 = path.join(targetFolder,hyphenName + '.scss'); fs.copySync( - path.join(__dirname, '..', 'defaults', 'component.scss'), + path.join(__dirname,'..','defaults','component.scss'), targetFile2, ); //replace variables in some of the copied files - await replaceVariablesInFiles(targetFile, targetFile2); + await replaceVariablesInFiles(targetFile,targetFile2); let indexPath = addLineToIndex( `import './components/${hyphenName}';`, @@ -1023,20 +1153,23 @@ export const createComponent = async (name, basePath = process.cwd()) => { export const checkImports = async ( sourceFolder: string = getSourceFolder(), depth: number = 0, // Used to check if the import is outside of the src folder - invalidImports: Map = new Map(), + invalidImports: Map = new Map(), ) => { const dir = fs.readdirSync(sourceFolder); // Start checking each file in the source folder - for (const file of dir) { - const filename = path.join(sourceFolder, file); + for (const file of dir) + { + const filename = path.join(sourceFolder,file); // File is either a directory, or not a .ts(x) // INFO: For future use - if this part fails, it could be due to user permissions // i.e. the program not having access to check the file metadata - if (!filename.match(/\.tsx?$/)) { - if (statSync(filename).isDirectory()) { - await checkImports(filename, depth + 1, invalidImports); + if (!filename.match(/\.tsx?$/)) + { + if (statSync(filename).isDirectory()) + { + await checkImports(filename,depth + 1,invalidImports); } // Ignore all files that aren't one of the following: @@ -1221,24 +1354,39 @@ export const startServer = async ( ) => { await ensureEnvironmentLoaded(); - let lincdConfig = await import(path.join(process.cwd(), 'lincd.config.js')); + let lincdConfig = await import(path.join(process.cwd(),'lincd.config.js')); - if (!ServerClass) { - ServerClass = (await import('lincd-server/lib/shapes/LincdServer.js')).LincdServer; + // function scssLoadcall(source, filename) { + // return 'console.log("SCSS CALL: ' + filename + '");\n' + source; + // process.exit(); + // } + // hook.hook('.scss', scssLoadcall); + // hook.hook('.css', scssLoadcall); + // import.meta. + // // hook.hook('*.css', scssLoadcall); + // // hook.hook('Body.module.css', scssLoadcall); + // hook.hook('.module.css', scssLoadcall); + + if (!ServerClass) + { + ServerClass = (await import('lincd-server/shapes/LincdServer')).LincdServer; } - await import(path.join(process.cwd(), 'scripts', 'storage-config.js')); + await import(path.join(process.cwd(),'scripts','storage-config.js')); let server = new ServerClass({ loadAppComponent: async () => - (await import(path.join(process.cwd(), 'src', 'App'))).default, + (await import(path.join(process.cwd(),'src','App'))).default, ...lincdConfig, }); //Important to use slice, because when using clusers, child processes need to be able to read the same arguments let args = process.argv.slice(2); //if --initOnly is passed, only initialize the server and don't start it - if (args.includes('--initOnly') || initOnly) { + if (args.includes('--initOnly') || initOnly) + { return server.initOnly(); - } else { + } + else + { return server.start(); } }; @@ -1334,52 +1482,187 @@ export const buildApp = async () => { export const upgradePackages = async () => { await ensureEnvironmentLoaded(); - let packages = getLincdPackages(); - packages = packages.filter((pkg) => { - pkg.packageName !== 'lincd' + // let packages = getLincdPackages(); + // let packages = getLocalLincdModules(); + let packages = getLocalLincdPackageMap(); + let dirname = getScriptDir(); + const tsConfigCJS = path.join(dirname,'../../defaults/package','tsconfig-cjs.json'); + const tsConfigESM = path.join(dirname,'../../defaults/package','tsconfig-esm.json'); + const typesFile = path.join(dirname,'../../defaults/package/src','types.d.ts'); + + const tsConfigTemplate = await fs.readJson(path.join(dirname,'../../defaults/package','tsconfig.json')).catch(err => { + console.log(err); }); - packages.forEach(pkg => { - console.log('Upgrading ' + pkg.packageName); - // execPromise(`cd ${pkg.path} && yarn upgrade`).then(() => { - // console.log('Upgraded ' + pkg.packageName); - // }).catch(err => { - // console.warn(err); - // }) - }) + runOnPackagesGroupedByDependencies(packages,(packageGroup,dependencies) => { + // packageGroup.forEach((pkg) => { + // console.log(' Upgrading ' + pkg.packageName); + console.log('-----'); + return async (pkg: PackageDetails) => { + if (pkg.packageName === 'lincd') return; + // await execPromise(`cd ${pkg.path} && yarn upgrade`); + console.log('Upgrading ' + pkg.packageName); + // + // //create a new file src/tsconfig-cjs.json + // //copy the contents of tsconfig.json into it + // if (!fs.existsSync(path.join(pkg.path,'tsconfig-cjs.json'))) + // { + // await fs.copy(tsConfigCJS,path.join(pkg.path,'tsconfig-cjs.json')); + // await fs.copy(tsConfigESM,path.join(pkg.path,'tsconfig-esm.json')); + // console.log('Copied new tsconfig to ' + pkg.packageName); + // } + // + // //read tsconfig + // await fs.readJson(path.join(pkg.path,'tsconfig.json')).then((tsconfig) => { + // let oldCompilerOpts = tsconfig.compilerOptions; + // tsconfig.compilerOptions = tsConfigTemplate.compilerOptions; + // tsconfig.compilerOptions.types = oldCompilerOpts.types; + // tsconfig.compilerOptions.plugins = [{"name": "typescript-plugin-css-modules"}]; + // + // console.log('Upgraded tsconfig for ' + pkg.packageName); + // return fs.writeJson(path.join(pkg.path,'tsconfig.json'),tsconfig,{spaces: 2}); + // }); + // //import types at the beginning of index.ts + // addLineToIndex(`import './types';`,null,pkg.path,true); + // //copy over the types file + // await fs.copy(typesFile,path.join(pkg.path,'src','types.d.ts')); + + // await fs.readJson(path.join(pkg.path,'package.json')).then((packageJson) => { + // let version = packageJson.version; + // let nextVersion; + // if (version.split('.').shift() === '0') + // { + // nextVersion = getNextMajorVersion(version); + // } + // else + // { + // nextVersion = getNextMinorVersion(version); + // } + // console.log('Upgraded version for ' + pkg.packageName + ' to ' + nextVersion); + // + // packageJson.version = nextVersion; + // packageJson.devDependencies['tsconfig-to-dual-package'] = '^1.2.0'; + // packageJson.devDependencies['typescript-plugin-css-modules'] = '^5.1.0'; + // + // packageJson.main = 'lib/cjs/index.js'; + // packageJson.module = 'lib/esm/index.js'; + // packageJson.exports = { + // '.': { + // 'types': './lib/esm/index.d.ts', + // 'import': './lib/esm/index.js', + // 'require': './lib/cjs/index.js', + // }, + // './*': { + // 'types': './lib/esm/*.d.ts', + // 'import': './lib/esm/*.js', + // 'require': './lib/cjs/*.js', + // }, + // }; + // packageJson.typesVersions = { + // '*': { + // '*': [ + // 'lib/esm/*', + // ], + // }, + // }; + // + // return fs.writeJson(path.join(pkg.path,'package.json'),packageJson,{ spaces: 2 }); + // }); -} + //change .css files and .scss files to .module.css and .module.scss + let files = await getFiles(path.join(pkg.path,'src')); + // let tsFiles = files.filter(f => f.match(/\.(ts|tsx)$/)); + // let cssFiles = files.filter(f => f.match(/\.(css|scss)$/)).filter(f => !f.match(/\.module\.(css|scss)$/)); + // cssFiles.forEach(cssFile => { + // let cssFileName = path.basename(cssFile); + // let newFile = cssFileName.replace(/\.s?css$/,'.module$&'); + // let newFilePath = cssFile.replace(/\.s?css$/,'.module$&'); + // let jsonFile = cssFileName.replace(/\.s?css$/,'$&.json'); + // fs.renameSync(cssFile,newFilePath); + // console.log('Renaming ' + cssFileName + ' to ' + newFilePath); + // //find other files that import this file and update them + // tsFiles.forEach(tsFile => { + // //read contents of f2 + // let contents = fs.readFileSync(tsFile,'utf8'); + // //if it imports f + // if (contents.indexOf(cssFileName) !== -1) + // { + // //find the whole line that imports f + // let line = contents.split('\n').find(l => l.indexOf(cssFileName) !== -1); + // // console.log("OLD: "+line); + // let jsonLine = contents.split('\n').find(l => l.indexOf(jsonFile) !== -1); + // // console.log("JSON: "+jsonLine); + // //if not commented out + // if(line.indexOf('//') === -1) { + // let previousImportPath = line.match(/['"](.*)['"]/)[1]; + // let newImportPath = previousImportPath.replace(cssFileName,newFile); + // let newContents = contents.replace(line,`import style from '${newImportPath}';`) + // .replace(jsonLine+'\n',''); + // // console.log("\n"); + // fs.writeFileSync(tsFile,newContents); + // console.log('Updated imports in ' + tsFile); + // // fs.writeFileSync + // // fs.writeFileSync(i,fs.readFileSync(i,'utf8').replace(f,newFile)); + // } + // } + // }) + // }); + files.filter(f => f.match(/\.(scss\.json|css\.json)$/)).forEach(cssJsonFile => { + console.log('Removing ' + cssJsonFile); + fs.unlinkSync(cssJsonFile); + }); + + }; + // }); + },() => { + console.log('Finished upgrading packages'); + }); + + // packages.forEach((pkg,key) => { + // console.log(key+' Upgrading ' + pkg.packageName); + // execPromise(`cd ${pkg.path} && yarn upgrade`).then(() => { + // console.log('Upgraded ' + pkg.packageName); + // }).catch(err => { + // console.warn(err); + // }) + // }); + +}; export const createPackage = async ( name, uriBase?, basePath = process.cwd(), ) => { - if (!name) { + if (!name) + { console.warn('Please provide a name as the first argument'); return; } //if ran with npx, basePath will be the root directory of the repository, even if we're executing from a sub folder (the root directory is where node_modules lives and package.json with workspaces) //so we manually find a packages folder, if it exists we go into that. - if (fs.existsSync(path.join(basePath, 'packages'))) { - basePath = path.join(basePath, 'packages'); + if (fs.existsSync(path.join(basePath,'packages'))) + { + basePath = path.join(basePath,'packages'); } //for lincd.org currently packages are stored in the modules folder - else if (fs.existsSync(path.join(basePath, 'modules'))) { - basePath = path.join(basePath, 'modules'); + else if (fs.existsSync(path.join(basePath,'modules'))) + { + basePath = path.join(basePath,'modules'); } //let's remove scope for variable names - let [packageName, scope, cleanPackageName] = name.match( + let [packageName,scope,cleanPackageName] = name.match( /(@[\w\-]+\/)?([\w\-]+)/, ); - let targetFolder = ensureFolderExists(basePath, cleanPackageName); + let targetFolder = ensureFolderExists(basePath,cleanPackageName); - if (!uriBase) { + if (!uriBase) + { uriBase = 'http://lincd.org/ont/' + name; } - setVariable('uri_base', uriBase + '/'); + setVariable('uri_base',uriBase + '/'); //find @scope and the next part between 2 slashes after //so @dacore/some-mod/lib/file.js @@ -1579,106 +1862,124 @@ export const buildPackage = async ( logResults: boolean = true, ) => { - const spinner = ora({ - discardStdin: true, - text: 'Compiling code', - }).start(); - let buildProcess:Promise = Promise.resolve(true); + let spinner; + if (logResults) + { + //TODO: replace with listr so we can show multiple processes at once + spinner = ora({ + discardStdin: true, + text: 'Compiling code', + }).start(); + } + let buildProcess: Promise = Promise.resolve(true); let buildStep = (step) => { buildProcess = buildProcess.then((previousResult) => { - spinner.text = step.name; - spinner.start(); + if (logResults) + { + spinner.text = step.name; + spinner.start(); + } return step.apply().then(stepResult => { - if(typeof stepResult === 'string') { + if (typeof stepResult === 'string') + { // spinner.text = step.name + ' - ' + stepResult; - spinner.warn(step.name + ' - ' + stepResult) - spinner.stop(); + if (logResults) + { + spinner.warn(step.name + ' - ' + stepResult); + spinner.stop(); + } //warning is shown, but build is still succesful with warnings return false; - } else if(stepResult === true || typeof stepResult === 'undefined') { - spinner.succeed(); + } + else if (stepResult === true || typeof stepResult === 'undefined') + { + if (logResults) + { + spinner.succeed(); + } return previousResult && true; } - }) + }); }); - } + }; buildStep({ name: 'Compiling code', apply: async () => { - //echo 'compiling CJS' && tsc -p tsconfig-cjs.json && echo 'compiling ESM' && tsc -p tsconfig-esm.json - let cjsConfig = fs.existsSync(path.join(packagePath,'tsconfig-cjs.json')); - let esmConfig = fs.existsSync(path.join(packagePath,'tsconfig-esm.json')); - let compileCJS = `yarn exec tsc -p tsconfig-cjs.json`; - let compileESM = `yarn exec tsc -p tsconfig-esm.json`; - let compileCommand; - if(cjsConfig && esmConfig) { - compileCommand = `${compileCJS} && ${compileESM}`; - } else if(cjsConfig) { - compileCommand = compileCJS; - } else if(esmConfig) { - compileCommand = compileESM; - } else { - compileCommand = `yarn exec tsc`; - } - return execPromise(compileCommand).then(res => { - return res === ''; - }) - } + return compilePackage(packagePath); + }, }); buildStep({ - name: 'Copying files', + name: 'Copying files to lib folder', apply: async () => { - return Promise.all( - [new Promise((resolve,reject) => { - copyfiles(['src/**/*.json', 'src/**/*.d.ts', 'src/**/*.scss', 'src/**/*.css','lib/esm'],1,(err) => { - if(err) { - reject(err); - } - else - { - resolve(true); - } - }) - }), - new Promise((resolve,reject) => { - copyfiles(['src/**/*.json','src/**/*.d.ts','src/**/*.scss','src/**/*.css','lib/cjs'],1,(err) => { - if (err) - { - return reject(err); - } - resolve(true); - }) - }) - ]).then((results) => { - return results.every(r => r === true) + const files = await glob(packagePath + '/src/**/*.{json,d.ts,css,scss}'); + return Promise.all(files.map((async (file) => { + try + { + await fs.copy(file,packagePath + '/lib/esm/' + file.replace(packagePath + '/src/','')); + await fs.copy(file,packagePath + '/lib/cjs/' + file.replace(packagePath + '/src/','')); + return true; + } catch (err) + { + console.warn(err); + return false; + } + ; + }))).then((allResults) => { + return allResults.every(r => r === true); }); - } + }, }); buildStep({ - name: 'Checking dependencies', - apply: depCheck + name: 'Dual package support', + apply: () => { + return execPromise('yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json',false,false,{ cwd: packagePath }).then(res => { + return res === ''; + }); + }, + }); + buildStep({ + name: 'Removing old files from lib folder', + apply: async () => { + return removeOldFiles(packagePath); + }, }); buildStep({ name: 'Checking imports', - apply: checkImports + apply: () => checkImports(packagePath), + }); + buildStep({ + name: 'Checking dependencies', + apply: () => depCheck(packagePath), }); let success = await buildProcess.catch(err => { - let msg = (err && err.stdout && err.error) ? (chalk.red('error')+err.error + ':\n'+err.stdout) : err.toString(); - spinner.stopAndPersist({ - symbol: chalk.red('✖'), - text: 'Build failed', - }); + //err.error + ':\n' + + let msg = (err && err.stdout && err.error) ? err.stdout : err.toString(); + if (logResults) + { + spinner.stopAndPersist({ + symbol: chalk.red('✖'), + text: 'Build failed', + }); + } + else + { + console.log(chalk.red(packagePath.split('/').pop(),' - Build failed:')); + } console.log(msg); }); - //will be undefined if there was an error - if(typeof success !== 'undefined') + //will be undefined if there was an error + if (typeof success !== 'undefined') { - spinner.stopAndPersist({ - symbol: chalk.greenBright('✔'), - text: success === true ? 'Build successful' : 'Build successful with warnings', - }); + if (logResults) + { + spinner.stopAndPersist({ + symbol: chalk.greenBright('✔'), + text: success === true ? 'Build successful' : 'Build successful with warnings', + }); + } + } return success; // 'build-lib': 'yarn exec tsc --pretty', @@ -1695,14 +1996,13 @@ export const buildPackage = async ( // filter: 'isFile', // }, - - // command: 'yarn lincd depcheck', //'check-imports': 'yarn lincd check-imports', - - if (target == 'production' || target == 'es5' || target == 'es6' || !target) { - if (!fs.existsSync(path.join(packagePath, 'Gruntfile.js'))) { + /*if (target == 'production' || target == 'es5' || target == 'es6' || !target) + { + if (!fs.existsSync(path.join(packagePath,'Gruntfile.js'))) + { console.warn( `No Gruntfile found at ${packagePath}\\Gruntfile.js. Cannot build.`, ); @@ -1750,22 +2050,51 @@ export const buildPackage = async ( //execute the command to build the method, and provide the current work directory as option return method( nodeEnv + - 'grunt build' + - (target ? '-' + target : '') + - (target2 ? '-' + target2 : '') + - ' --color', + 'grunt build' + + (target ? '-' + target : '') + + (target2 ? '-' + target2 : '') + + ' --color', false, false, - {cwd: packagePath}, + { cwd: packagePath }, ).catch((err) => { process.exit(1); }); - } else { + } + else + { console.warn('unknown build target. Use es5, es6 or production.'); + }*/ +}; +export const compilePackage = async (packagePath = process.cwd()) => { + //echo 'compiling CJS' && tsc -p tsconfig-cjs.json && echo 'compiling ESM' && tsc -p tsconfig-esm.json + let cjsConfig = fs.existsSync(path.join(packagePath,'tsconfig-cjs.json')); + let esmConfig = fs.existsSync(path.join(packagePath,'tsconfig-esm.json')); + let compileCJS = `yarn exec tsc -p tsconfig-cjs.json`; + let compileESM = `yarn exec tsc -p tsconfig-esm.json`; + let compileCommand; + if (cjsConfig && esmConfig) + { + compileCommand = `${compileCJS} && ${compileESM}`; } + else if (cjsConfig) + { + compileCommand = compileCJS; + } + else if (esmConfig) + { + compileCommand = compileESM; + } + else + { + compileCommand = `yarn exec tsc`; + } + return execPromise(compileCommand,false,false,{ cwd: packagePath }).then(res => { + return res === ''; + }); }; -export var publishUpdated = function (test: boolean = false) { +export var publishUpdated = function(test: boolean = false) { let packages = getLocalLincdModules(); var p: Promise = Promise.resolve(''); @@ -2088,31 +2417,43 @@ export var buildUpdated = async function ( // true, ); - if (pkg.packageName === 'lincd-jsonld' && jsonldPkgUpdated) { + if (pkg.packageName === 'lincd-jsonld' && jsonldPkgUpdated) + { needRebuild = true; } - if (needRebuild || rebuildAllModules) { + if (needRebuild || rebuildAllModules) + { //TODO: when building a pkg, also rebuild all packages that depend on this package.. and iteratively build packages that depend on those packages.. // log(packageName+' modified since last commit on '+now.toString()); - if (test) { + if (test) + { debugInfo('Need to build ' + pkg.packageName); return chalk.blue(pkg.packageName + ' should be build'); } log('Building ' + pkg.packageName); return execPromise( 'cd ' + - pkg.path + - ' && yarn build' + - (target ? ' ' + target : '') + - (target2 ? ' ' + target2 : ''), + pkg.path + + ' && yarn build' + + (target ? ' ' + target : '') + + (target2 ? ' ' + target2 : ''), ) .then((res) => { - debugInfo(chalk.green(pkg.packageName + ' successfully built')); - return chalk.green(pkg.packageName + ' built'); + if (res !== '') + { + warn(chalk.red('Failed to build ' + pkg.packageName)); + console.log(res); + process.exit(1); + } + else + { + debugInfo(chalk.green(pkg.packageName + ' successfully built')); + return chalk.green(pkg.packageName + ' built'); + } }) - .catch(({error, stdout, stderr}) => { + .catch(({ error,stdout,stderr }) => { warn(chalk.red('Failed to build ' + pkg.packageName)); console.log(stdout); process.exit(1); @@ -2143,25 +2484,30 @@ export var buildUpdated = async function ( return; }; -const printBuildResults = function (failed, done) { - log( - 'Successfully built: ' + +const printBuildResults = function(failed,done) { + if (done.size > 0 || done.length > 0) + { + log( + 'Successfully built: ' + chalk.green([...done].map((m) => m.packageName).join(', ')) + '\n', - ); - if (failed.length > 0) { + ); + } + if (failed.length > 0) + { warn('Failed to build: ' + chalk.red(failed.join(', ')) + '\n'); } }; -export var executeCommandForEachPackage = function ( +export var executeCommandForEachPackage = function( packages, command, filterMethod, filterValue, ) { //if a specific set of packages is given - if (filterMethod == 'exclude') { + if (filterMethod == 'exclude') + { //filter packages, so that we only execute on the packages as provided in the command log('Excluding ' + filterValue); filterValue = filterValue.split(','); @@ -2315,31 +2661,74 @@ export var addCapacitor = async function (basePath = process.cwd()) { ); }; -export var executeCommandForPackage = function (packageName, command) { +export var executeCommandForPackage = function(packageName,command) { let packageDetails = getLincdPackages().find( (modDetails: PackageDetails) => modDetails.packageName.indexOf(packageName) !== -1 || modDetails.packageName.indexOf(packageName) !== -1, ); - if (packageDetails) { + if (packageDetails) + { log( - "Executing 'cd " + - packageDetails.path + - ' && yarn exec lincd' + - (command ? ' ' + command : '') + - "'", + 'Executing \'cd ' + + packageDetails.path + + ' && yarn exec lincd' + + (command ? ' ' + command : '') + + '\'', ); return execp( 'cd ' + - packageDetails.path + - ' && yarn exec lincd' + - (command ? ' ' + command : ''), + packageDetails.path + + ' && yarn exec lincd' + + (command ? ' ' + command : ''), ); - } else { + } + else + { warn( - "Could not find a pkg who's name (partially) matched " + - chalk.cyan(packageName), + 'Could not find a pkg who\'s name (partially) matched ' + + chalk.cyan(packageName), ); } }; + +/** + * Function to remove files older than 10 seconds from the 'lib' folder. + * @param {string} packagePath - The path to the package directory. + */ +export const removeOldFiles = async (packagePath) => { + const libPath = path.join(packagePath,'lib'); + + try + { + // Read all files in the 'lib' folder asynchronously + const files = await glob(packagePath + '/lib/**/*.*'); + + // Iterate through each file + for (const file of files) + { + // const filePath = path.join(libPath, file); + + // Check if the file exists before attempting to delete it + // if (await fs.pathExists(filePath)) { + const stats = await fs.stat(file); + const currentTime = new Date().getTime(); + const lastModifiedTime = stats.mtime.getTime(); + + // Check if the difference between the current time and last modified time is greater than 10 seconds + if (currentTime - lastModifiedTime > 10000) + { + // Attempt to delete the file + await fs.unlink(file); + // console.log(`Removed: ${file}`); + } + // } + } + return true; + } catch (error) + { + console.error(`Error removing files: ${error.message}`); + return false; + } +}; \ No newline at end of file diff --git a/src/cli.ts b/src/cli.ts index 8517ac2..02f6dc2 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -11,7 +11,7 @@ import { buildApp, buildPackage, buildUpdated, - checkImports, + checkImports,compilePackage, createApp, createComponent, createOntology, @@ -23,7 +23,7 @@ import { developPackage, executeCommandForEachPackage, executeCommandForPackage, - getLincdPackages, + getLincdPackages,getScriptDir, publishPackage, publishUpdated, register, @@ -161,7 +161,7 @@ program program .command('info') .action(() => { - const localDir = dirname(import.meta.url).replace('file:/', ''); + let localDir = getScriptDir(); let packageJsonPath =path.join(localDir, 'package.json') try { var ownPackage = JSON.parse( @@ -184,6 +184,9 @@ program buildPackage(target, target2); }); +program.command('compile-only').action(() => { + compilePackage(); +}).description('Compile the package without other build steps. Run this command from the package folder'); program.command('build-metadata').action(() => { console.log('Needs to be reimplemented'); // buildMetadata(); diff --git a/src/metadata.ts b/src/metadata.ts index 08eb07e..6f5b751 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -1,154 +1,154 @@ -import {NamedNode} from 'lincd/models'; -import {GetEnvVars} from 'env-cmd'; -import path from 'path'; -import fs from 'fs-extra'; -import {createNameSpace} from 'lincd/utils/NameSpace'; -import {Prefix} from 'lincd/utils/Prefix'; -import {getPackageJSON} from './utils'; -import {warn} from './cli-methods'; -import {JSONLDWriter} from 'lincd-jsonld/lib/utils/JSONLDWriter'; - -function getLocalPackagePaths() { - let packagePaths = []; - - //add the APP package itself - let appPackagePath = process.cwd(); - let appLincdPackagePath = path.join( - appPackagePath, - 'frontend', - 'src', - 'package.ts', - ); - if (fs.existsSync(appPackagePath)) { - let packageJSON = getPackageJSON(appPackagePath); - - packagePaths.push([ - packageJSON.name, - appPackagePath, - appLincdPackagePath, - true, - ]); - } else { - console.log('Not a LINCD app'); - } - - //NOTE: we could also switch to checking 'workspaces'? - let packagesFolder = path.join(process.cwd(), 'packages'); - if (fs.existsSync(packagesFolder)) { - let localPackages = fs.readdirSync(packagesFolder); - localPackages.forEach((packageFolderName) => { - packagePaths.push([ - packageFolderName, - path.join(packagesFolder, packageFolderName), - path.join(packagesFolder, packageFolderName, 'lib', 'package.js'), - ]); - }); - } - return packagePaths; -} - -export const buildMetadata = async (): Promise => { - // require('@babel/register')({extensions: ['js', '.ts', '.tsx']}); - - //NOTE: we can not rely on the LincdWebApp shape from lincd-server here, because that would make the initial build of all modules a lot trickier - //see, CLI needs to be built as one of the first things in order to build other things. So it cannot rely on lincd-server, which relies on 10 other packages - let app: NamedNode; - - //set the URL of the app as the URI of its node - let envVars: any = await GetEnvVars({ - envFile: { - filePath: '.env-cmdrc.json', - }, - }); - if ( - envVars[process.env.NODE_ENV] && - envVars[process.env.NODE_ENV].SITE_ROOT - ) { - //get the site root of the current environment - app = NamedNode.getOrCreate(envVars[process.env.NODE_ENV].SITE_ROOT); - } else { - warn( - 'Cannot find environment variable SITE_ROOT. Make sure SITE_ROOT is set (likely in .env-cmdrc.json) for the current environment: ' + - process.env.NODE_ENV, - ); - app = NamedNode.create(); - } - - let updatedPaths = []; - var localPackagePaths = getLocalPackagePaths(); - - //prepare output path - let metadataFolder = path.join(process.cwd(), 'data', 'metadata'); - await fs.ensureDir(metadataFolder); //{recursive:true} but not needed with fs-extra - - for (const [ - packageCodeName, - packagePath, - lincdPackagePath, - isAppPackage, - ] of localPackagePaths) { - let errors = false; - //TODO: check if this resolves, if not, skip it (for initial setup) - import('lincd-modules/lib/scripts/package-metadata.js').then( - async (script) => { - await script - .getPackageMetadata(packagePath, lincdPackagePath) - .then(async (response) => { - if (response.errors.length > 0) { - // console.log(JSON.stringify(response)); - warn( - 'Error processing ' + - packagePath + - ':\n' + - response.errors.join('\n'), - ); - // throw response - errors = true; - } else { - if (!response.packageUri) { - console.warn( - 'No package URI from meta data. Not building meta data for this package', - ); - return; - } - let pkgNode = NamedNode.getOrCreate(response.packageUri); - //connect the packages to the app - let lincdApp = createNameSpace('http://lincd.org/ont/lincd-app/'); - Prefix.add('lincdApp', 'http://lincd.org/ont/lincd-app/'); - if (isAppPackage) { - //Note: this needs to match with LincdWebApp.ownPackage accessor; - app.overwrite(lincdApp('ownPackage'), pkgNode); - } else { - //Note: this needs to match with LincdWebApp.packages accessor; - app.set(lincdApp('maintainsPackage'), pkgNode); - } - - //write this graph to a jsonld file - let packageMetaData = JSON.stringify(response.result, null, 2); - let metadataFile = path.join( - metadataFolder, - packageCodeName + '.json', - ); - await fs.writeFile(metadataFile, packageMetaData).then(() => { - updatedPaths.push(metadataFile); - }); - } - }); - }, - ); - - //enable this when testing if you don't want to continue with building other metadata when an errors occur - // if (errors) break; - } - - let packageMetaData = await JSONLDWriter.stringify( - app as any, - process.env.NODE_ENV === 'development', - ); - let metadataFile = path.join(metadataFolder, '_app.json'); - await fs.writeFile(metadataFile, packageMetaData).then(() => { - updatedPaths.push(metadataFile); - }); - - return updatedPaths; -}; +// import {NamedNode} from 'lincd/models'; +// import {GetEnvVars} from 'env-cmd'; +// import path from 'path'; +// import fs from 'fs-extra'; +// import {createNameSpace} from 'lincd/utils/NameSpace'; +// import {Prefix} from 'lincd/utils/Prefix'; +// import {getPackageJSON} from './utils'; +// import {warn} from './cli-methods'; +// import {JSONLDWriter} from 'lincd-jsonld/utils/JSONLDWriter'; +// +// function getLocalPackagePaths() { +// let packagePaths = []; +// +// //add the APP package itself +// let appPackagePath = process.cwd(); +// let appLincdPackagePath = path.join( +// appPackagePath, +// 'frontend', +// 'src', +// 'package.ts', +// ); +// if (fs.existsSync(appPackagePath)) { +// let packageJSON = getPackageJSON(appPackagePath); +// +// packagePaths.push([ +// packageJSON.name, +// appPackagePath, +// appLincdPackagePath, +// true, +// ]); +// } else { +// console.log('Not a LINCD app'); +// } +// +// //NOTE: we could also switch to checking 'workspaces'? +// let packagesFolder = path.join(process.cwd(), 'packages'); +// if (fs.existsSync(packagesFolder)) { +// let localPackages = fs.readdirSync(packagesFolder); +// localPackages.forEach((packageFolderName) => { +// packagePaths.push([ +// packageFolderName, +// path.join(packagesFolder, packageFolderName), +// path.join(packagesFolder, packageFolderName, 'lib', 'package.js'), +// ]); +// }); +// } +// return packagePaths; +// } +// +// export const buildMetadata = async (): Promise => { +// // require('@babel/register')({extensions: ['js', '.ts', '.tsx']}); +// +// //NOTE: we can not rely on the LincdWebApp shape from lincd-server here, because that would make the initial build of all modules a lot trickier +// //see, CLI needs to be built as one of the first things in order to build other things. So it cannot rely on lincd-server, which relies on 10 other packages +// let app: NamedNode; +// +// //set the URL of the app as the URI of its node +// let envVars: any = await GetEnvVars({ +// envFile: { +// filePath: '.env-cmdrc.json', +// }, +// }); +// if ( +// envVars[process.env.NODE_ENV] && +// envVars[process.env.NODE_ENV].SITE_ROOT +// ) { +// //get the site root of the current environment +// app = NamedNode.getOrCreate(envVars[process.env.NODE_ENV].SITE_ROOT); +// } else { +// warn( +// 'Cannot find environment variable SITE_ROOT. Make sure SITE_ROOT is set (likely in .env-cmdrc.json) for the current environment: ' + +// process.env.NODE_ENV, +// ); +// app = NamedNode.create(); +// } +// +// let updatedPaths = []; +// var localPackagePaths = getLocalPackagePaths(); +// +// //prepare output path +// let metadataFolder = path.join(process.cwd(), 'data', 'metadata'); +// await fs.ensureDir(metadataFolder); //{recursive:true} but not needed with fs-extra +// +// for (const [ +// packageCodeName, +// packagePath, +// lincdPackagePath, +// isAppPackage, +// ] of localPackagePaths) { +// let errors = false; +// //TODO: check if this resolves, if not, skip it (for initial setup) +// import('lincd-modules/scripts/package-metadata.js').then( +// async (script) => { +// await script +// .getPackageMetadata(packagePath, lincdPackagePath) +// .then(async (response) => { +// if (response.errors.length > 0) { +// // console.log(JSON.stringify(response)); +// warn( +// 'Error processing ' + +// packagePath + +// ':\n' + +// response.errors.join('\n'), +// ); +// // throw response +// errors = true; +// } else { +// if (!response.packageUri) { +// console.warn( +// 'No package URI from meta data. Not building meta data for this package', +// ); +// return; +// } +// let pkgNode = NamedNode.getOrCreate(response.packageUri); +// //connect the packages to the app +// let lincdApp = createNameSpace('http://lincd.org/ont/lincd-app/'); +// Prefix.add('lincdApp', 'http://lincd.org/ont/lincd-app/'); +// if (isAppPackage) { +// //Note: this needs to match with LincdWebApp.ownPackage accessor; +// app.overwrite(lincdApp('ownPackage'), pkgNode); +// } else { +// //Note: this needs to match with LincdWebApp.packages accessor; +// app.set(lincdApp('maintainsPackage'), pkgNode); +// } +// +// //write this graph to a jsonld file +// let packageMetaData = JSON.stringify(response.result, null, 2); +// let metadataFile = path.join( +// metadataFolder, +// packageCodeName + '.json', +// ); +// await fs.writeFile(metadataFile, packageMetaData).then(() => { +// updatedPaths.push(metadataFile); +// }); +// } +// }); +// }, +// ); +// +// //enable this when testing if you don't want to continue with building other metadata when an errors occur +// // if (errors) break; +// } +// +// let packageMetaData = await JSONLDWriter.stringify( +// app as any, +// process.env.NODE_ENV === 'development', +// ); +// let metadataFile = path.join(metadataFolder, '_app.json'); +// await fs.writeFile(metadataFile, packageMetaData).then(() => { +// updatedPaths.push(metadataFile); +// }); +// +// return updatedPaths; +// }; diff --git a/src/utils.ts b/src/utils.ts index afd2fd5..1f1471d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import chalk from 'chalk'; -import {exec} from 'child_process'; +import { exec,ExecOptions } from 'child_process'; import ts from 'typescript'; import {builtinModules} from 'module'; import {PackageDetails} from 'interfaces'; @@ -197,11 +197,12 @@ export var getLINCDDependencies = function ( // a simple sort with dependencyMap doesn't seem to work,so we start with LINCD (least dependencies) and from there add packages that have all their dependencies already added let sortedPackagePaths = []; let addedPackages = new Set(['lincd']); - sortedPackagePaths.push( - lincdPackagePaths.find(([packageName]) => { - return packageName === 'lincd'; - }), - ); + let lincdItself = lincdPackagePaths.find(([packageName]) => { + return packageName === 'lincd'; + }); + if(lincdItself) { + sortedPackagePaths.push(lincdItself); + } while (addedPackages.size !== lincdPackagePaths.length) { let startSize = addedPackages.size; @@ -407,7 +408,7 @@ export function execPromise( command, log = false, allowError: boolean = false, - options?: any, + options?: ExecOptions, pipeOutput: boolean = false, ): Promise { return new Promise(function (resolve, reject) { diff --git a/tsconfig.json b/tsconfig.json index 28a67cb..7f29503 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "skipLibCheck": true, "baseUrl": "./src", "module": "esnext", + "moduleResolution": "node", "types": ["node"] }, "include": ["./src/**/*.ts","./src/**/*.cts"], From 669b7abb4d60adc829f1ffcf0ee56551688852d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Mon, 27 May 2024 11:37:15 +0100 Subject: [PATCH 10/87] ignoring order - which should be safe for our use case (fixing strange errors) --- src/config-webpack-app.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config-webpack-app.ts b/src/config-webpack-app.ts index 1354ae8..861dbb8 100644 --- a/src/config-webpack-app.ts +++ b/src/config-webpack-app.ts @@ -243,7 +243,9 @@ export const getWebpackAppConfig = async () => { plugins: [ // new WatchRunPlugin(), // new RebuildScssJsonPlugin(), - new MiniCssExtractPlugin(), + new MiniCssExtractPlugin({ + ignoreOrder:true + }), new webpack.EnvironmentPlugin(Object.keys(process.env)), // new ForkTsCheckerWebpackPlugin(), isDevelopment && new ReactRefreshWebpackPlugin(), From 9957747997a06d74d73935b7382da3f0bddb869c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Wed, 29 May 2024 10:50:07 +0100 Subject: [PATCH 11/87] fix some commands by applying dirname fix everywhere published as 1.1.2 --- package.json | 6 +++--- src/cli-methods.ts | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 719860d..634fa28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lincd-cli", - "version": "1.1.1", + "version": "1.1.2", "description": "Command line tools for the lincd.js library", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", @@ -31,8 +31,8 @@ "dual-package": "echo 'adding package.json to each lib folder for dual output support' && yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json", "dev": "yarn tsc -p tsconfig-esm.json -w", "dev-cjs": "yarn tsc -p tsconfig-cjs.json -w", - "postinstall": "husky install", - "prepack": "npm exec tsc && pinst --disable && yarn version patch", + "_postinstall": "husky install", + "prepack": "yarn build && pinst --disable && yarn version patch", "postpack": "pinst --enable", "prettier": "prettier \"src/**/*.{js,jsx,ts,tsx,css,scss}\" --check", "prettier:fix": "yarn prettier --write", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 3c84a7e..1380186 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -1110,7 +1110,7 @@ export const createShape = async (name,basePath = process.cwd()) => { //copy default shape file // log("Creating files for shape '" + name + "'"); let targetFile = path.join(targetFolder,hyphenName + '.ts'); - fs.copySync(path.join(__dirname,'..','defaults','shape.ts'),targetFile); + fs.copySync(path.join(getScriptDir(),'..','..','defaults','shape.ts'),targetFile); //replace variables in some of the copied files await replaceVariablesInFiles(targetFile); @@ -1137,13 +1137,13 @@ export const createSetComponent = async (name,basePath = process.cwd()) => { log('Creating files for set component \'' + name + '\''); let targetFile = path.join(targetFolder,hyphenName + '.tsx'); fs.copySync( - path.join(__dirname,'..','defaults','set-component.tsx'), + path.join(getScriptDir(),'..','..','defaults','set-component.tsx'), targetFile, ); let targetFile2 = path.join(targetFolder,hyphenName + '.scss'); fs.copySync( - path.join(__dirname,'..','defaults','component.scss'), + path.join(getScriptDir(),'..','..','defaults','component.scss'), targetFile2, ); @@ -1174,13 +1174,13 @@ export const createComponent = async (name,basePath = process.cwd()) => { log('Creating files for component \'' + name + '\''); let targetFile = path.join(targetFolder,hyphenName + '.tsx'); fs.copySync( - path.join(__dirname,'..','defaults','component.tsx'), + path.join(getScriptDir(),'..','defaults','component.tsx'), targetFile, ); let targetFile2 = path.join(targetFolder,hyphenName + '.scss'); fs.copySync( - path.join(__dirname,'..','defaults','component.scss'), + path.join(getScriptDir(),'..','defaults','component.scss'), targetFile2, ); @@ -1763,7 +1763,7 @@ export const createPackage = async ( setNameVariables(cleanPackageName); log('Creating new LINCD package \'' + name + '\''); - fs.copySync(path.join(__dirname,'..','defaults','package'),targetFolder); + fs.copySync(path.join(getScriptDir(),'..','..','defaults','package'),targetFolder); //replace variables in some of the copied files await Promise.all( @@ -2570,11 +2570,11 @@ export var addCapacitor = async function(basePath = process.cwd()) { log('Adding capacitor'); fs.copySync( - path.join(__dirname,'..','defaults','app-static'), + path.join(getScriptDir(),'..','..','defaults','app-static'), targetFolder, ); fs.copySync( - path.join(__dirname,'..','defaults','capacitor','scripts'), + path.join(getScriptDir(),'..','..','defaults','capacitor','scripts'), path.join(targetFolder,'scripts'), ); From 907a3b2978cae8b795eaf3aac8e1c9af0ab7d8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Wed, 29 May 2024 16:38:23 +0100 Subject: [PATCH 12/87] published 1.1.3 and fixed dependency --- package.json | 6 +-- src/plugins/shapes-plugin.js | 81 ------------------------------------ 2 files changed, 3 insertions(+), 84 deletions(-) delete mode 100644 src/plugins/shapes-plugin.js diff --git a/package.json b/package.json index 634fa28..fe002e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lincd-cli", - "version": "1.1.2", + "version": "1.1.3", "description": "Command line tools for the lincd.js library", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", @@ -31,7 +31,7 @@ "dual-package": "echo 'adding package.json to each lib folder for dual output support' && yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json", "dev": "yarn tsc -p tsconfig-esm.json -w", "dev-cjs": "yarn tsc -p tsconfig-cjs.json -w", - "_postinstall": "husky install", + "postinstall": "husky install", "prepack": "yarn build && pinst --disable && yarn version patch", "postpack": "pinst --enable", "prettier": "prettier \"src/**/*.{js,jsx,ts,tsx,css,scss}\" --check", @@ -121,6 +121,7 @@ "postcss-reporter": "^7.1.0", "postcss-scss": "^4.0.9", "postcss-strip-inline-comments": "^0.1.5", + "postcss-url": "^10.1.3", "react-refresh-typescript": "^2.0.9", "require-extensions": "^0.0.4", "sass": "^1.71.1", @@ -145,7 +146,6 @@ "husky": "^9.0.11", "optimize-css-assets-webpack-plugin": "^6.0.1", "pinst": "^3.0.0", - "postcss-url": "^10.1.3", "prettier": "^3.2.5" }, "peerDepencencies": {} diff --git a/src/plugins/shapes-plugin.js b/src/plugins/shapes-plugin.js deleted file mode 100644 index 0ceaab9..0000000 --- a/src/plugins/shapes-plugin.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict'; -exports.__esModule = true; -/// -var colors = require('colors'); -var ShapesPlugin = /** @class */ (function () { - function ShapesPlugin(options) { - if (options === void 0) { - options = {}; - } - this.shapeFiles = []; - this.logMessages = true; - this.exportRoot = '/lib'; - this.logMessages = options.debug ? options.debug : false; - } - - ShapesPlugin.prototype.apply = function (compiler) { - var _this = this; - this.debug('applying'); - //when the compiler is ready to emit files - // compiler.plugin('emit', (compilation,callback) => - compiler.hooks.emit.tapAsync( - 'DeclarationPlugin', - function (compilation, callback) { - // this.debug('emitted'); - _this.debug(Object.keys(compilation.assets)); - //collect all generated shape files - //NOTE: at some point we decided to overwrite declaration files between emits because sometimes only one new declaration file is emitted - //this may cause issues when you remove a file during the continuous building process, but better than the other way around for now - for (var filename in compilation.assets) { - if ( - filename.indexOf('.js') !== -1 && - filename.indexOf('.map') === -1 - ) { - _this.debug(filename, Object.keys(compilation.assets[filename])); - // require(filename); - // this.declarationFiles[filename] = compilation.assets[filename]; - // this.debug('not using: '+filename); - // delete compilation.assets[filename]; - } - } - //and insert that back into the assets - // compilation.assets[this.options.out] = { - // source: function () { - // return combinedDeclaration; - // }, - // size: function () { - // return combinedDeclaration.length; - // }, - // }; - //webpack may continue now - callback(); - }, - ); - }; - ShapesPlugin.prototype.debug = function () { - var msgs = []; - for (var _i = 0; _i < arguments.length; _i++) { - msgs[_i] = arguments[_i]; - } - msgs.unshift('shapes:'); - // if (this.logMessages) { - msgs = msgs.map(function (msg) { - return colors.blue(msg); - }); - console.log.apply(null, msgs); - // } - }; - ShapesPlugin.prototype.log = function () { - var msgs = []; - for (var _i = 0; _i < arguments.length; _i++) { - msgs[_i] = arguments[_i]; - } - msgs.unshift('shapes:'); - msgs = msgs.map(function (msg) { - return colors.blue(msg); - }); - console.log.apply(null, msgs); - }; - return ShapesPlugin; -})(); -exports['default'] = ShapesPlugin; From 58c0cf5043c225a121c58a284535d2185e9301b7 Mon Sep 17 00:00:00 2001 From: olfia Date: Fri, 31 May 2024 14:37:08 +0800 Subject: [PATCH 13/87] ignore lincd-server so that it can be built without lincd.org dependencies --- package.json | 1 + src/cli-methods.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/package.json b/package.json index fe002e3..949fc12 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,7 @@ "postcss-url": "^10.1.3", "react-refresh-typescript": "^2.0.9", "require-extensions": "^0.0.4", + "rimraf": "^5.0.7", "sass": "^1.71.1", "sass-loader": "^14.1.1", "source-map-loader": "^5.0.0", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 1380186..479e52c 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -1444,6 +1444,7 @@ export const startServer = async ( if (!ServerClass) { + //@ts-ignore ServerClass = (await import('lincd-server/shapes/LincdServer')).LincdServer; } await import(path.join(process.cwd(),'scripts','storage-config.js')); From 6b27829046731c8262d409897e5eaf1bb80bbe89 Mon Sep 17 00:00:00 2001 From: olfia Date: Fri, 31 May 2024 17:35:08 +0800 Subject: [PATCH 14/87] better build errors --- src/cli-methods.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 479e52c..6fdc3c1 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -2008,8 +2008,7 @@ export const buildPackage = async ( }); let success = await buildProcess.catch(err => { - //err.error + ':\n' + - let msg = (err && err.stdout && err.error) ? err.stdout : err.toString(); + let msg = err.error ? err.error : err.stdout + '\n'+err.stderr; if (logResults) { spinner.stopAndPersist({ From beabed4177d0d33f9c0539f8dddcaf15bf83f680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Mon, 11 Nov 2024 13:24:32 +0000 Subject: [PATCH 15/87] small fixes in build time --- package.json | 2 +- src/cli-methods.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index fe002e3..cba20ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lincd-cli", - "version": "1.1.3", + "version": "1.1.5", "description": "Command line tools for the lincd.js library", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 1380186..1f2ac2d 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -2527,7 +2527,7 @@ export var executeCommandForEachPackage = function( packages = packages.filter((pkg) => { if ( !seen && - (pkg.packageName == startFrom || pkg.packageName == startFrom) + pkg.packageName.includes(startFrom) ) { seen = true; @@ -2714,8 +2714,8 @@ export const removeOldFiles = async (packagePath) => { const currentTime = new Date().getTime(); const lastModifiedTime = stats.mtime.getTime(); - // Check if the difference between the current time and last modified time is greater than 10 seconds - if (currentTime - lastModifiedTime > 10000) { + // Check if the difference between the current time and last modified time is greater than 120 seconds + if (currentTime - lastModifiedTime > 120000) { // Attempt to delete the file await fs.unlink(file); // console.log(`Removed: ${file}`); From 5440defda9853f02437adf245268e1f9a0621bd4 Mon Sep 17 00:00:00 2001 From: abdipramana Date: Thu, 14 Nov 2024 21:34:19 +0800 Subject: [PATCH 16/87] skip upload to storage when S3_BUCKET_ENDPOINT is not set in environment --- src/cli-methods.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cli-methods.ts b/src/cli-methods.ts index d50ebe8..022d1ed 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -1512,9 +1512,10 @@ export const buildApp = async () => { }); }).then(async () => { // make sure environment is not development for storage config - if (process.env.NODE_ENV === 'development') + // and if we want to upload to storage, we need set S3_BUCKET_ENDPOINT + if (process.env.NODE_ENV === 'development' || !process.env.S3_BUCKET_ENDPOINT) { - console.warn('Upload build to storage skip in development environment'); + console.warn('Upload build to storage skip in development environment or S3_BUCKET_ENDPOINT is not set'); process.exit(); } @@ -1523,6 +1524,7 @@ export const buildApp = async () => { console.warn('Not uploading to CDN for app builds'); process.exit(); } + // load the storage config const storageConfig = await import( path.join(process.cwd(),'scripts','storage-config.js') From b806e73804f933929c877fb90d8a23e28cb1e580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Mon, 18 Nov 2024 11:32:07 +0000 Subject: [PATCH 17/87] fix require error for parsing scss during build process --- package.json | 2 +- src/utils.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4337a44..77ea3fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lincd-cli", - "version": "1.1.5", + "version": "1.1.8", "description": "Command line tools for the lincd.js library", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", diff --git a/src/utils.ts b/src/utils.ts index b405cee..a136b82 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,6 +8,7 @@ import {PackageDetails} from 'interfaces'; import { findNearestPackageJson,findNearestPackageJsonSync } from 'find-nearest-package-json'; import * as glob from 'glob'; +import * as crypto from 'crypto'; var gruntConfig; @@ -447,7 +448,7 @@ export function generateScopedNameProduction( //for app development we can use short unique hashes //but for webpack bundles of lincd modules, we need to ensure unique class names across bundles of many packages //generate a short unique hash based on cssClassName and filepath - let hash = require('crypto') + let hash = crypto .createHash('md5') .update(cssClassName + filepath) .digest('hex') From ccbb10e0d628ffdcad94fa19305d9cc27db2db1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Tue, 19 Nov 2024 14:33:16 +0000 Subject: [PATCH 18/87] published as 1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77ea3fa..f1551ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lincd-cli", - "version": "1.1.8", + "version": "1.2.0", "description": "Command line tools for the lincd.js library", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", From d8f283cdba63cbc13544007a9d2aaff08148f208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Fri, 25 Apr 2025 13:44:34 +0200 Subject: [PATCH 19/87] upgraded typescript extended dual modujle type support updated tailwind to v4 and improved support: * No more endless watch cycles being triggered. * Lazy imported bundles are also included in tailwinds css generation * We turn off tailwind preflight, but then recreate it almost entirely. But without destructive/strict selectors that ended up overwriting styles from CSS modules * imported as first postcss plugin so that styles & theme variables from tailwind come first in the bundle. SCSS - removed entirely. we rely on postcss for things like nesting. CSS modules - are now only applied to css files with .module.css names - imrpoved css loader for css modules inside node.js improved option to see which files changed and triggered a rebuild (to be cleaned up a bit) config-webpack will likely be outdated, if its used to bundle a lincd package (we dont really do that at the moment) --- defaults/app-with-backend/package.json | 5 +- defaults/app-with-backend/tsconfig.json | 1 + .../src/ontologies/example-ontology.ts | 2 +- lib-template/cjs/package.json | 3 + lib-template/esm/package.json | 3 + package.json | 22 +- src/cli-methods.ts | 206 ++++++++----- src/cli.ts | 7 + src/config-webpack-app.ts | 285 ++++++++++++++---- src/config-webpack.ts | 6 +- src/loaders/css-loader.mts | 37 ++- src/loaders/register.ts | 9 +- src/utils.ts | 95 ++++-- tsconfig.json | 3 +- 14 files changed, 497 insertions(+), 187 deletions(-) create mode 100644 lib-template/cjs/package.json create mode 100644 lib-template/esm/package.json diff --git a/defaults/app-with-backend/package.json b/defaults/app-with-backend/package.json index c094fd6..6454541 100644 --- a/defaults/app-with-backend/package.json +++ b/defaults/app-with-backend/package.json @@ -16,7 +16,7 @@ "main": "scripts/start-server.js", "license": "MIT", "scripts": { - "start": "yarn lincd start --env development", + "start": "node --import ./node_modules/lincd-cli/lib/esm/loaders/register.js node_modules/.bin/lincd-cli start --env development", "build": "yarn lincd build-app --env production", "postinstall": "husky install", "prepack": "pinst --disable", @@ -63,13 +63,14 @@ "lincd-jsonld": "~1.0", "lincd-server": "~1.0", "lincd-server-utils": "~1.0", + "node-gyp": "^11.1.0", "react": "^18.2", "react-dom": "^18.2", "react-error-boundary": "^3.1.3", "react-router-dom": "^6.3.0" }, "devDependencies": { - "@types/node": "^18.11.10", + "@types/node": "^20.12.7", "@types/react": "^18.0.17", "@types/react-dom": "^18.0.6", "@typescript-eslint/eslint-plugin": "latest", diff --git a/defaults/app-with-backend/tsconfig.json b/defaults/app-with-backend/tsconfig.json index eee1844..188f053 100644 --- a/defaults/app-with-backend/tsconfig.json +++ b/defaults/app-with-backend/tsconfig.json @@ -4,6 +4,7 @@ "moduleResolution": "node", "sourceMap": true, "target": "es2019", + "resolveJsonModule": true, "declaration": false, "experimentalDecorators": true, "emitDecoratorMetadata": true, diff --git a/defaults/package/src/ontologies/example-ontology.ts b/defaults/package/src/ontologies/example-ontology.ts index 87ea5b3..caebfef 100644 --- a/defaults/package/src/ontologies/example-ontology.ts +++ b/defaults/package/src/ontologies/example-ontology.ts @@ -15,7 +15,7 @@ export var loadData = () => { } else { // ESM import //@ts-ignore - return import('../data/${hyphen_name}.json',{ assert: { type: "json" } }).then((data) => JSONLD.parse(data)); + return import('../data/${hyphen_name}.json',{ with: { type: "json" } }).then((data) => JSONLD.parse(data)); } }; diff --git a/lib-template/cjs/package.json b/lib-template/cjs/package.json new file mode 100644 index 0000000..c9a4422 --- /dev/null +++ b/lib-template/cjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} \ No newline at end of file diff --git a/lib-template/esm/package.json b/lib-template/esm/package.json new file mode 100644 index 0000000..aead43d --- /dev/null +++ b/lib-template/esm/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} \ No newline at end of file diff --git a/package.json b/package.json index f1551ab..ffdb070 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@babel/register": "^7.23.7", "@lodder/grunt-postcss": "^3.1.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", + "@tailwindcss/postcss": "^4.1.4", "@types/node": "^20.12.7", "@types/react": "^18.0.17", "@types/react-dom": "^18.0.6", @@ -110,33 +111,33 @@ "node-hook": "^1.0.0", "open": "^8.4.0", "ora": "^8.0.1", - "postcss": "^8.4.35", + "postcss": "^8.5.3", "postcss-comment": "^2.0.0", "postcss-font-magician": "^4.0.0", - "postcss-import": "^16.0.1", + "postcss-import": "^16.1.0", "postcss-loader": "^8.1.1", - "postcss-modules": "^6.0.0", - "postcss-nested": "^6.0.1", - "postcss-preset-env": "^9.5.0", + "postcss-modules": "^6.0.1", + "postcss-nested": "^7.0.2", + "postcss-preset-env": "^10.1.5", "postcss-reporter": "^7.1.0", "postcss-scss": "^4.0.9", "postcss-strip-inline-comments": "^0.1.5", "postcss-url": "^10.1.3", - "react-refresh-typescript": "^2.0.9", + "react-refresh-typescript": "^2.0.10", "require-extensions": "^0.0.4", "rimraf": "^5.0.7", "sass": "^1.71.1", "sass-loader": "^14.1.1", "source-map-loader": "^5.0.0", "staged-git-files": "^1.3.0", - "tailwindcss": "^3.4.1", + "tailwindcss": "^4.1.4", "terminal-kit": "^3.1.1", "terser-webpack-plugin": "^5.3.10", "ts-loader": "^9.5.1", "tsconfig-paths-webpack-plugin": "^4.1.0", "tsconfig-to-dual-package": "^1.2.0", - "tsx": "^4.7.2", - "typescript": "^4.8.4", + "tsx": "^4.19.3", + "typescript": "^5.7.3", "typescript-plugin-css-modules": "^5.1.0", "webpack": "^5.90.3", "webpack-bundle-analyzer": "^4.10.1", @@ -149,5 +150,6 @@ "pinst": "^3.0.0", "prettier": "^3.2.5" }, - "peerDepencencies": {} + "peerDepencencies": {}, + "type": "module" } diff --git a/src/cli-methods.ts b/src/cli-methods.ts index 022d1ed..27b9643 100644 --- a/src/cli-methods.ts +++ b/src/cli-methods.ts @@ -12,8 +12,7 @@ import { getFileImports, getFiles, getLastCommitTime, - getPackageJSON, - isValidLINCDImport, + getPackageJSON,isImportOutsideOfPackage,isImportWithMissingExtension,isInvalidLINCDImport, needsRebuilding, } from './utils.js'; @@ -28,7 +27,7 @@ import { glob } from 'glob'; import webpack from 'webpack'; import stagedGitFiles from 'staged-git-files'; -import ora from 'ora'; +import ora,{ Ora } from 'ora'; var variables = {}; export const createApp = async (name,basePath = process.cwd()) => { @@ -939,7 +938,7 @@ export const createOntology = async ( { //then also add an import to index let indexPath = addLineToIndex( - `import './ontologies/${hyphenName}';`, + `import './ontologies/${hyphenName}.js';`, 'ontologies', ); log(`Added an import of this file from ${chalk.magenta(indexPath)}`); @@ -1124,7 +1123,7 @@ export const createShape = async (name,basePath = process.cwd()) => { let indexPath; if (!sourceFolder.includes('frontend')) { - indexPath = addLineToIndex(`import './shapes/${hyphenName}';`,'shapes'); + indexPath = addLineToIndex(`import './shapes/${hyphenName}.js';`,'shapes'); log(`Added an import of this file from ${chalk.magenta(indexPath)}`); } }; @@ -1151,7 +1150,7 @@ export const createSetComponent = async (name,basePath = process.cwd()) => { await replaceVariablesInFiles(targetFile,targetFile2); let indexPath = addLineToIndex( - `import './components/${hyphenName}';`, + `import './components/${hyphenName}.js';`, 'components', ); @@ -1200,7 +1199,7 @@ export const createComponent = async (name,basePath = process.cwd()) => { { //then also add an import to index let indexPath = addLineToIndex( - `import './components/${hyphenName}';`, + `import './components/${hyphenName}.js';`, 'components', ); log(`Added an import of this file from ${chalk.magenta(indexPath)}`); @@ -1209,11 +1208,11 @@ export const createComponent = async (name,basePath = process.cwd()) => { //read the source of all ts/tsx files in the src folder //if there is an import that imports a lincd package with /src/ in it, then warn -//if there is an import that imports outside of the src folder, then warn +//if there is an import that imports something from outside the src folder, then warn export const checkImports = async ( sourceFolder: string = getSourceFolder(), - depth: number = 0, // Used to check if the import is outside of the src folder - invalidImports: Map = new Map(), + depth: number = 0, // Used to check if the import is outside the src folder + invalidImports: Map = new Map(), ) => { const dir = fs.readdirSync(sourceFolder); @@ -1227,56 +1226,76 @@ export const checkImports = async ( // i.e. the program not having access to check the file metadata if (!filename.match(/\.tsx?$/)) { - if (statSync(filename).isDirectory()) + try { - await checkImports(filename,depth + 1,invalidImports); + if (statSync(filename).isDirectory()) + { + await checkImports(filename, depth + 1, invalidImports); + } + else + { + // Ignore all files that aren't one of the following: + // - .ts + // - .tsx + continue; + } + } catch(e) { + console.log(e); } - - // Ignore all files that aren't one of the following: - // - .ts - // - .tsx - continue; } const allImports = await getFileImports(filename); - const lincdImports = allImports.filter( - (i) => i.includes('lincd') || i.includes('..'), - ); + if (!invalidImports.has(filename)) + { + invalidImports.set(filename,[]); + } - lincdImports.forEach((i) => { - if (!isValidLINCDImport(i,depth)) - { - if (!invalidImports.has(filename)) - { - invalidImports.set(filename,[]); - } + allImports.forEach((i) => { - invalidImports.get(filename).push(i); + if(isImportOutsideOfPackage(i,depth)) { + invalidImports.get(filename).push({ + type:'outside_package', + importPath:i + }); + } + if (isInvalidLINCDImport(i,depth)) + { + invalidImports.get(filename).push({ + type:'lincd', + importPath:i + }); + } + if(isImportWithMissingExtension(i)) { + invalidImports.get(filename).push({ + type:'missing_extension', + importPath:i + }); } }); } let res = ''; + //check if invalidImports has any + let flat = [...invalidImports.values()].flat(); // All recursion must have finished, display any errors - if (depth === 0 && invalidImports.size > 0) + if (depth === 0 && flat.length > 0) { - res += chalk.red('Invalid imports found.\n'); invalidImports.forEach((value,key) => { // res += '- '+chalk.blueBright(key.split('/').pop()) + ':\n'; - value.forEach((i) => { - res += chalk.red(key.split('/').pop() + ' imports from \'' + i + '\'\n'); - if (i.indexOf('../../') === 0) - { - res += - 'To fix: import from the NPM package directly.\n'; + value.forEach(({type,importPath}) => { + let message = key.split('/').pop() + ' imports from \'' + importPath+'\''; + if(type === 'outside_package') { + message += ' which is outside the package source root'; } - else if ('/src/') - { - res += 'To fix: you likely need to replace /src with /lib\n'; + if(type === 'lincd') { + message += ' which should not contain /src/ or /lib/ in the import path'; } - + if(type === 'missing_extension') { + message += ' which should end with a file extension. Like .js or .scss'; + } + res += chalk.red(message+'\n'); }); }); @@ -1429,7 +1448,7 @@ export const startServer = async ( ) => { await ensureEnvironmentLoaded(); - let lincdConfig = await import(path.join(process.cwd(),'lincd.config.js')); + let lincdConfig = (await import(path.join(process.cwd(),'lincd.config.js'))).default; // function scssLoadcall(source, filename) { // return 'console.log("SCSS CALL: ' + filename + '");\n' + source; @@ -1918,24 +1937,29 @@ export const buildPackage = async ( logResults: boolean = true, ) => { - let spinner; + let spinner:Ora; if (logResults) { //TODO: replace with listr so we can show multiple processes at once spinner = ora({ discardStdin: true, - text: 'Compiling code', + text: 'Compiling ESM', }).start(); } let buildProcess: Promise = Promise.resolve(true); let buildStep = (step) => { buildProcess = buildProcess.then((previousResult) => { + if(!previousResult) { + return false; + } if (logResults) { spinner.text = step.name; spinner.start(); } return step.apply().then(stepResult => { + //if a build step returns a string, + //a warning is shown but the build is still successful with warnings if (typeof stepResult === 'string') { // spinner.text = step.name + ' - ' + stepResult; @@ -1944,8 +1968,8 @@ export const buildPackage = async ( spinner.warn(step.name + ' - ' + stepResult); spinner.stop(); } - //warning is shown, but build is still succesful with warnings - return false; + //can still continue + return true; } else if (stepResult === true || typeof stepResult === 'undefined') { @@ -1955,14 +1979,28 @@ export const buildPackage = async ( } return previousResult && true; } + else if(typeof stepResult === 'object' && stepResult.error) { + if(logResults) { + spinner.fail(step.name + ' - ' + stepResult.error); + spinner.stop(); + } + //failed and should stop + return false + } }); }); }; buildStep({ - name: 'Compiling code', + name: 'Compiling ESM', apply: async () => { - return compilePackage(packagePath); + return compilePackageESM(packagePath); + }, + }); + buildStep({ + name: 'Compiling CJS', + apply: async () => { + return compilePackageCJS(packagePath); }, }); buildStep({ @@ -2010,22 +2048,23 @@ export const buildPackage = async ( }); let success = await buildProcess.catch(err => { - let msg = err.error ? err.error : err.stdout + '\n'+err.stderr; + let msg = typeof err === 'string' || err instanceof Error ? err.toString() : (err.error && !err.error.toString().includes("Command failed:") ? err.error : err.stdout + '\n'+err.stderr); if (logResults) { spinner.stopAndPersist({ symbol: chalk.red('✖'), - text: 'Build failed', + // text: 'Build failed', }); } else { console.log(chalk.red(packagePath.split('/').pop(),' - Build failed:')); + console.log(err); } console.log(msg); }); //will be undefined if there was an error - if (typeof success !== 'undefined') + if (typeof success !== 'undefined' && success !== false) { if (logResults) { @@ -2035,36 +2074,57 @@ export const buildPackage = async ( }); } + } else { + spinner.stopAndPersist({ + symbol: chalk.red('✖'), + text: 'Build failed', + }); } return success; }; export const compilePackage = async (packagePath = process.cwd()) => { //echo 'compiling CJS' && tsc -p tsconfig-cjs.json && echo 'compiling ESM' && tsc -p tsconfig-esm.json - let cjsConfig = fs.existsSync(path.join(packagePath,'tsconfig-cjs.json')); - let esmConfig = fs.existsSync(path.join(packagePath,'tsconfig-esm.json')); - let compileCJS = `yarn exec tsc -p tsconfig-cjs.json`; - let compileESM = `yarn exec tsc -p tsconfig-esm.json`; - let compileCommand; - if (cjsConfig && esmConfig) - { - compileCommand = `${compileCJS} && ${compileESM}`; - } - else if (cjsConfig) - { - compileCommand = compileCJS; - } - else if (esmConfig) - { - compileCommand = compileESM; - } - else - { - compileCommand = `yarn exec tsc`; - } + // let cjsConfig = fs.existsSync(path.join(packagePath,'tsconfig-cjs.json')); + // let esmConfig = fs.existsSync(path.join(packagePath,'tsconfig-esm.json')); + // let compileCJS = `yarn exec tsc -p tsconfig-cjs.json`; + // let compileESM = `yarn exec tsc -p tsconfig-esm.json`; + // let compileCommand; + // if (cjsConfig && esmConfig) + // { + // compileCommand = `${compileCJS} && ${compileESM}`; + // } + // else if (cjsConfig) + // { + // compileCommand = compileCJS; + // } + // else if (esmConfig) + // { + // compileCommand = compileESM; + // } + // else + // { + // compileCommand = `yarn exec tsc`; + // } + await compilePackageESM(packagePath); + await compilePackageCJS(packagePath); +} +export const compilePackageESM = async (packagePath = process.cwd()) => { + //echo 'compiling CJS' && tsc -p tsconfig-cjs.json && echo 'compiling ESM' && tsc -p tsconfig-esm.json + let compileCommand = `yarn exec tsc -p tsconfig-esm.json`; return execPromise(compileCommand,false,false,{ cwd: packagePath }).then(res => { return res === ''; }); }; +export const compilePackageCJS = async (packagePath = process.cwd()) => { + let compileCommand = `yarn exec tsc -p tsconfig-cjs.json`; + return execPromise(compileCommand,false,false,{ cwd: packagePath }).then(res => { + return res === ''; + }).catch(err => { + return { + error:err.stdout + }; + }) +}; export var publishUpdated = function(test: boolean = false) { let packages = getLocalLincdModules(); @@ -2445,7 +2505,7 @@ export var buildUpdated = async function( return chalk.green(pkg.packageName + ' built'); } else if (typeof res === 'string') { warn(chalk.red('Failed to build ' + pkg.packageName)); - console.log(res); + // console.log(res); process.exit(1); } }) @@ -2679,6 +2739,8 @@ export var executeCommandForPackage = function(packageName,command) { '\'', ); + //TODO : replace with spawn("path to executable", ["params"], {stdio: "inherit"}); + // maybe make spawnPromise that returns a promise return execp( 'cd ' + packageDetails.path + diff --git a/src/cli.ts b/src/cli.ts index 02f6dc2..b7371c3 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -282,6 +282,13 @@ program .join(' ') : null; + //TODO: call + // let pkgName = process.argv[1]; + // console.log(pkgName); + // let restArgs = process.argv.slice(2) + // program.parse([],{from:'user'}); + // program.parse(['--port', '80'], { from: 'user' }) + executeCommandForPackage(name, fullCommand); }) .alias('p') diff --git a/src/config-webpack-app.ts b/src/config-webpack-app.ts index 861dbb8..dc43fdf 100644 --- a/src/config-webpack-app.ts +++ b/src/config-webpack-app.ts @@ -14,9 +14,11 @@ import { getLinkedTailwindColors, } from './utils.js'; -import tailwindPlugin from 'tailwindcss/plugin.js'; +// import tailwindPlugin from 'tailwindcss/plugin.js'; import { LinkedFileStorage } from 'lincd/utils/LinkedFileStorage'; import postcssUrl from 'postcss-url'; +//@ts-ignore +import plugin from 'tailwindcss/plugin'; const isProduction = process.env.NODE_ENV === 'production'; const isDevelopment = process.env.NODE_ENV === 'development'; @@ -33,14 +35,50 @@ class WatchRunPlugin compiler.hooks.watchRun.tap('watchRun',(comp) => { if (comp.modifiedFiles) { - const changedFiles = Array.from( - comp.modifiedFiles, - (file) => `\n ${file}`, - ).join(''); - if (changedFiles.length) - { - console.log(chalk.magenta('Changed files:'),changedFiles); - } + // const changedFiles = Array.from( + // comp.modifiedFiles, + // (file) => `\n ${file}`, + // ).join(''); + // if (changedFiles.length) + // { + // console.log(chalk.magenta('Changed files:'),changedFiles); + // } + const changedFiles = Array.from(comp.modifiedFiles); + + console.log(chalk.magenta('Changed files:')); + const entriesToCheck = []; + + changedFiles.forEach((file) => { + try { + const stat = fs.statSync(file.toString()); + console.log(` ${chalk.magenta(file)}`); + entriesToCheck.push({ path: file, mtime: stat.mtime }); + if (stat.isDirectory()) { + const contents = fs.readdirSync(file.toString()); + contents.forEach((name) => { + const fullPath = path.join(file.toString(), name); + try { + const innerStat = fs.statSync(fullPath); + entriesToCheck.push({ path: fullPath, mtime: innerStat.mtime }); + } catch (e) { + entriesToCheck.push({ path: fullPath, mtime: new Date(0), error: e.message }); + } + }); + } + } catch (err) { + entriesToCheck.push({ path: file, mtime: new Date(0), error: err.message }); + } + }); + + entriesToCheck + .sort((a, b) => b.mtime - a.mtime) + .splice(0,7) + .forEach((entry) => { + const label = entry.error + ? `[error: ${entry.error}]` + : `[${entry.mtime.toISOString()}]`; + console.log(` ${entry.path} ${label}`); + }); } }); } @@ -71,7 +109,7 @@ export const getLincdConfig = async () => { else if (fs.existsSync(lincdConfigPathJs)) { let lincdConfig = await import(lincdConfigPathJs); - config = { ...config,...lincdConfig }; + config = { ...config,...lincdConfig.default }; } else if (fs.existsSync(lincdConfigPathJson)) { @@ -104,13 +142,164 @@ export const getWebpackAppConfig = async () => { let config = await getLincdConfig(); - let postcssPlugins = [ + let postcssPlugins = []; + + //tailwind first (so its processed last and doesnt overwrite custom CSS modules) + if (config.cssMode === 'tailwind' || config.cssMode === 'mixed') + { + //make sure that tailwind classes from any LINCD packages that are listed in package.json:dependencies are included + let lincdPackagePaths: any = getLINCDDependencies(packageJson); + lincdPackagePaths = lincdPackagePaths.map(([packageName,packagePath]) => { + return packagePath + '/lib/**/*.{js,mjs}'; + }); + // console.log( + // chalk.blueBright('tailwind content: ') + chalk.magenta(['./frontend/src/**/*.{tsx,ts}', ...lincdPackagePaths]), + // ); + postcssPlugins.push([ + '@tailwindcss/postcss', + { + content: [ + (process.env.SOURCE_PATH || './src/') + '**/*.{tsx,ts}', + ...lincdPackagePaths, + ], + // safelist: isProduction + // ? {} + // : { + // //in development mode we allow all classes here, so that you can easily develop + // pattern: /./, + // variants: ['sm','md','lg','xl','2xl'], + // }, + // features: { + // themeVariables: { + // generateAll: true, + // }, + // }, + theme: { + extend: { + colors: getLinkedTailwindColors(), + }, + }, + plugins: [ + plugin(function({ addBase, theme }) { + //add styles to the base styles + //this replicates the preflight settings of tailwind v4, but without the destructive/strict #/# selectors + addBase({ + // Reset all elements except common inline tags and semantic containers + '*:not(code):not(pre):not(kbd):not(samp):not(mark):not(q):not(ins):not(del):not(span):not(a):not(b):not(i):not(em):not(u):not(s):not(small):not(strong):not(sub):not(sup), ::before, ::after': { + boxSizing: 'border-box', + margin: '0', + padding: '0', + borderWidth: '0', + borderStyle: 'solid', + borderColor: 'currentColor', + }, + html: { + lineHeight: '1.5', + textSizeAdjust: '100%', + WebkitTextSizeAdjust: '100%', + MozTextSizeAdjust: '100%', + fontFamily: 'system-ui, sans-serif', + }, + body: { + margin: '0', + lineHeight: 'inherit', + backgroundColor: 'white', + }, + hr: { + height: '0', + color: 'inherit', + borderTopWidth: '1px', + }, + abbr: { + textDecoration: 'underline dotted', + }, + 'b, strong': { + fontWeight: 'bolder', + }, + 'code, kbd, samp, pre': { + fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', + fontSize: '1em', + }, + small: { + fontSize: '80%', + }, + 'sub, sup': { + fontSize: '75%', + lineHeight: '0', + position: 'relative', + verticalAlign: 'baseline', + }, + sub: { bottom: '-0.25em' }, + sup: { top: '-0.5em' }, + table: { + textIndent: '0', + borderColor: 'inherit', + borderCollapse: 'collapse', + }, + 'button, input, optgroup, select, textarea': { + font: 'inherit', + color: 'inherit', + margin: '0', + padding: '0', + lineHeight: 'inherit', + backgroundColor: 'transparent', + borderColor: 'inherit', + }, + 'button, select': { + textTransform: 'none', + }, + 'button, [type="button"], [type="reset"], [type="submit"]': { + appearance: 'button', + WebkitAppearance: 'button', + }, + '::-moz-focus-inner': { + borderStyle: 'none', + padding: '0', + }, + ':-moz-focusring': { + outline: 'auto', + }, + ':-moz-ui-invalid': { + boxShadow: 'none', + }, + fieldset: { + margin: '0', + padding: '0', + border: '0', + }, + legend: { + padding: '0', + }, + }); + }), + ], + // plugins: [ + // tailwindPlugin(function({ addBase,config }) { + // //we can use LINCD CSS variables for default font color, size etc. + // // addBase({ + // // 'h1': { fontSize: config('theme.fontSize.2xl') }, + // // 'h2': { fontSize: config('theme.fontSize.xl') }, + // // 'h3': { fontSize: config('theme.fontSize.lg') }, + // // }) + // }), + // ], + }, + ]); + } + postcssPlugins.push([ postcssUrl({ url: (asset) => { - return `${accessURL}${publicPath}${asset.url}`; + //This convert URLs used in CSS and adds the full public URL instead of a local path + //TODO: for assets of packages, we want to detect the package path and resolve through node_modules/package-name/...something/assets + if(!asset.url.startsWith('data:')) + { + // console.log('Transform CSS URL:'+asset.url); + return `${accessURL}${publicPath}${asset.url}`; + } + return asset.url; }, }), - ]; + ]) if ( config.cssMode === 'scss-modules' || config.cssMode === 'scss' || @@ -153,6 +342,7 @@ export const getWebpackAppConfig = async () => { ]); if (config.cssMode === 'scss-modules' || config.cssMode === 'mixed') { + //NOTE: this is replaced with the css-loader // postcssPlugins.push([ // 'postcss-modules', // { @@ -165,52 +355,10 @@ export const getWebpackAppConfig = async () => { // ]); } } - if (config.cssMode === 'tailwind' || config.cssMode === 'mixed') - { - //make sure that tailwind classes from any LINCD packages that are listed in package.json:dependencies are included - let lincdPackagePaths: any = getLINCDDependencies(packageJson); - lincdPackagePaths = lincdPackagePaths.map(([packageName,packagePath]) => { - return packagePath + '/lib/**/*.{js,mjs}'; - }); - // console.log( - // chalk.blueBright('tailwind content: ') + chalk.magenta(['./frontend/src/**/*.{tsx,ts}', ...lincdPackagePaths]), - // ); - postcssPlugins.push([ - 'tailwindcss', - { - content: [ - (process.env.SOURCE_PATH || './src/') + '**/*.{tsx,ts}', - ...lincdPackagePaths, - ], - safelist: isProduction - ? {} - : { - //in development mode we allow all classes here, so that you can easily develop - pattern: /./, - variants: ['sm','md','lg','xl','2xl'], - }, - theme: { - extend: { - colors: getLinkedTailwindColors(), - }, - }, - plugins: [ - tailwindPlugin(function({ addBase,config }) { - //we can use LINCD CSS variables for default font color, size etc. - // addBase({ - // 'h1': { fontSize: config('theme.fontSize.2xl') }, - // 'h2': { fontSize: config('theme.fontSize.xl') }, - // 'h3': { fontSize: config('theme.fontSize.lg') }, - // }) - }), - ], - }, - ]); - } return { mode: isProduction ? 'production' : 'development', - devtool: isProduction ? 'source-map' : 'cheap-module-source-map', + devtool: isProduction ? 'source-map' : 'eval-cheap-module-source-map', entry: [ isDevelopment && 'webpack-hot-middleware/client', path.resolve( @@ -232,7 +380,8 @@ export const getWebpackAppConfig = async () => { clean: true, }, watchOptions: { - ignored: ['**/*.d.ts','**/*.js.map','**/*.scss.json'], + //ignore everything except the src folder. ignore specific files in the src folder + ignored: /(^((?!src).)*$|\.d\.ts$|\.js\.map$|\.scss\.json$|node_modules|public|\.idea|[/\\]\..*)/, aggregateTimeout: 500, }, devServer: { @@ -241,7 +390,7 @@ export const getWebpackAppConfig = async () => { }, }, plugins: [ - // new WatchRunPlugin(), + new WatchRunPlugin(), // new RebuildScssJsonPlugin(), new MiniCssExtractPlugin({ ignoreOrder:true @@ -269,8 +418,18 @@ export const getWebpackAppConfig = async () => { mode: 'local', // namedExport:true, // exportOnlyLocals:true, + // exclude:/tailwind/i, getLocalIdent: getLocalIdent, - localIdentName: '[local]--[hash:base64:6]', + // auto: (resourcePath: string) => { + // // Use CSS Modules for everything except node_modules/tailwind + // return !/node_modules[\\/]tailwind/.test(resourcePath); + // }, + auto: (resourcePath: string) => { + //make sure this only applies to .module.css files, and not to tailwind + return /\.module\.css$/i.test(resourcePath) && !/tailwind/i.test(resourcePath); + } + // localIdentName: '[local]--[hash:base64:6]', + // localIdentName: '[local]', // localIdentName: "[path][name]__[local]--[hash:base64:5]", }, @@ -284,10 +443,10 @@ export const getWebpackAppConfig = async () => { }, }, }, - { - loader: 'sass-loader', - options: { sourceMap: true }, - }, + // { + // loader: 'sass-loader', + // options: { sourceMap: true }, + // }, ], }, // { diff --git a/src/config-webpack.ts b/src/config-webpack.ts index 7482d15..918f9a2 100644 --- a/src/config-webpack.ts +++ b/src/config-webpack.ts @@ -22,7 +22,7 @@ import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; import TerserPlugin from 'terser-webpack-plugin'; import { exec } from 'child_process'; import CopyPlugin from 'copy-webpack-plugin'; -import tailwindPlugin from 'tailwindcss/plugin'; +// import tailwindPlugin from 'tailwindcss/plugin'; declare var __dirname: string; declare var require: any; @@ -239,14 +239,14 @@ export function generateWebpackConfig( }, }, plugins: [ - tailwindPlugin(function ({addBase, config}) { + // tailwindPlugin(function ({addBase, config}) { //we can use LINCD CSS variables for default font color, size etc. // addBase({ // 'h1': { fontSize: config('theme.fontSize.2xl') }, // 'h2': { fontSize: config('theme.fontSize.xl') }, // 'h3': { fontSize: config('theme.fontSize.lg') }, // }) - }), + // }), ], }, ]); diff --git a/src/loaders/css-loader.mts b/src/loaders/css-loader.mts index db47f04..72cd64c 100644 --- a/src/loaders/css-loader.mts +++ b/src/loaders/css-loader.mts @@ -4,26 +4,37 @@ import { generateScopedName,generateScopedNameProduction } from '../utils.js'; const cssLoader = { resolve(specifier, opts) { - if (specifier.endsWith('.css') || specifier.endsWith('.scss')) { - let { parentURL } = opts; - let url = new URL(specifier, parentURL).href; - return { url }; + //check if the url is css or scss + if ((specifier.endsWith('.css') || specifier.endsWith('.scss'))) { + // console.log(`##LOADER option resolve ${specifier}`); + //and not a node_module (because we don't need to process node_modules) + if (specifier.startsWith('.')) + { + // console.log(`##LOADER resolve ${specifier} - ${specifier.startsWith('.') ? 'local' : 'node_module'}`); + let { parentURL } = opts; + let url = new URL(specifier,parentURL).href; + return { url }; + } else { + // console.log(`##LOADER NOT RESOLVING ${specifier}`); + } } }, format(url, opts) { - if (url.endsWith('.css') || url.endsWith('.scss')) { + //check if the url is css or scss and not a node_module + // if ((url.endsWith('.css') || url.endsWith('.scss')) && url.startsWith('.')) { + if ((url.endsWith('.css') || url.endsWith('.scss'))) { + // console.log(`##LOADER format ${url} - ${url.startsWith('.') ? 'local' : 'node_module'}`); + return { format: 'module' }; } }, transform(source, opts) { const { url } = opts - if (url.endsWith('.css') || url.endsWith('.scss')) { - - // const { outputText } = ts.transpileModule(String(source), { - // compilerOptions: { - // module: ts.ModuleKind.ES2020, - // }, - // }) + //check if the url is css or scss and not a node_module + if ((url.endsWith('.css') || url.endsWith('.scss'))) { + // console.log(`##LOADER transform ${url} - ${url.startsWith('.') ? 'local' : 'node_module'}`); + //if yes, convert the CSS source to a JSON object with original selectors as keys + //and the converted class names as values let cssClassesObject = parseCssToObject(String(source),opts.url); let finalSource = JSON.stringify(cssClassesObject,null,2); return { source: `export default ${finalSource};`}; @@ -33,7 +44,7 @@ const cssLoader = { function parseCssToObject(rawSource:string,filename) { const output = {}; - //TODO: replace with parse scss + //@TODO: replace with parse scss let myResults = rawSource.match(/\.[a-zA-Z_]{1}[\w]+[\s:]/g); if(myResults) { myResults.map(result => { diff --git a/src/loaders/register.ts b/src/loaders/register.ts index 763c65d..0a36d1f 100644 --- a/src/loaders/register.ts +++ b/src/loaders/register.ts @@ -1,3 +1,10 @@ import { register } from 'node:module'; //@ts-ignore -register('./css-loader.mjs', import.meta.url); \ No newline at end of file +import { register as tsx } from 'tsx/esm/api'; + +//first register our hooks before TSX +//@ts-ignore +register('./css-loader.mjs', import.meta.url); + +//then register TXS +tsx(); \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index a136b82..47eec59 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -28,28 +28,68 @@ const tsHost = ts.createCompilerHost( ); export var getFileImports = async function (filePath) { - const sourceFile = tsHost.getSourceFile( - filePath, - ts.ScriptTarget.Latest, - (msg) => { - throw new Error(`Failed to parse ${filePath}: ${msg}`); - }, - ); - if (!sourceFile) throw ReferenceError(`Failed to find file ${filePath}`); - const importing: string[] = []; - delintNode(sourceFile); - return importing; - - function delintNode(node: ts.Node) { - if (ts.isImportDeclaration(node)) { - const moduleName = node.moduleSpecifier.getText().replace(/['"]/g, ''); - if ( - !moduleName.startsWith('node:') && - !builtinModules.includes(moduleName) - ) - importing.push(moduleName); - } else ts.forEachChild(node, delintNode); + try { + const importing: string[] = []; + + const delintNode = (node: ts.Node) => { + if (ts.isImportDeclaration(node)) { + const moduleName = node.moduleSpecifier.getText().replace(/['"]/g, ''); + if ( + !moduleName.startsWith('node:') && + !builtinModules.includes(moduleName) + ) + importing.push(moduleName); + } else ts.forEachChild(node, delintNode); + } + const sourceFile = tsHost.getSourceFile( + filePath, + ts.ScriptTarget.Latest, + (msg) => { + throw new Error(`Failed to parse ${filePath}: ${msg}`); + }, + ); + //check if its a directory, then we wanr that we can't parse it + let stat = fs.lstatSync(filePath); + if(stat.isDirectory()) { + return importing; + } + + if (!sourceFile) { + console.warn(`Failed to find file ${filePath}`); + return importing; + } + delintNode(sourceFile); + return importing; + } catch(err) { + console.warn(`Error parsing file ${filePath}: ${err}`); + return []; + } + + +}; + +/** + * + * @param importPath The import path to check + * @param curFileDepth How many folders deep the current file is (0 = src, 1 = src/foo, etc.) + * @returns + */ +export var isInvalidLINCDImport = function ( + importPath: string, + curFileDepth: number, +) { + return importPath.includes('lincd') && (importPath.includes('/src/') || importPath.includes('/lib/')); +} +export var isImportOutsideOfPackage = function ( + importPath: string, + curFileDepth: number, +) { + if (importPath.includes('..')) { + //the number of '..' in the import path should be less than or equal to the current file depth + //if its bigger then the import is outside of the package + return importPath.split('..').length - 1 > curFileDepth; } + return false; }; /** @@ -76,6 +116,18 @@ export var isValidLINCDImport = function ( return validLincdPath || validRelativePath; }; +export var isImportWithMissingExtension = function (importPath: string) { + //if a relative import then it needs an extension + if(importPath.startsWith('../') || importPath.startsWith('./')) { + //check if the last part of the import after the last slash has a file extension + if(importPath.split('/').pop().split('.').length < 2) { + //if it doesn't have an extension, then it's missing, so return true + return true; + } + } + return false; +} + export var getPackageJSON = function (root = process.cwd(), error = true) { let packagePath = path.join(root, 'package.json'); if (fs.existsSync(packagePath)) { @@ -461,6 +513,7 @@ export function generateScopedName( filepath, css?, ) { + // return cssClassName; var filename = path.basename(filepath).replace(/\.(module\.)?(css|scss)/,''); let resolved = path.resolve(filepath).replace(/[\w\-_\/]+\/file\:/,''); let nearestPackageJson = findNearestPackageJsonSync(resolved); diff --git a/tsconfig.json b/tsconfig.json index 4d2e59c..4f74fb8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,8 @@ "baseUrl": "./src", "module": "esnext", "moduleResolution": "node", - "types": ["node"] + "types": ["node"], + "allowSyntheticDefaultImports": true }, "include": ["./src/**/*.ts","./src/**/*.cts","./src/**/*.mts"], "files": ["./src/index.ts", "./src/cli.ts"] From 304523f570f7fb433b42a1df98f5124a6969a4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Tue, 6 May 2025 13:08:54 +0100 Subject: [PATCH 20/87] first working tailwind config, inserting all lincd modules as tailwind sources and importing that as content in a separate tailwind.config.js file inside the app --- src/config-webpack-app.ts | 320 ++++++++++++-------------- src/index.ts | 1 + src/plugins/lincd-tailwind-sources.ts | 39 ++++ src/tailwind.config.ts | 13 ++ 4 files changed, 202 insertions(+), 171 deletions(-) create mode 100644 src/plugins/lincd-tailwind-sources.ts create mode 100644 src/tailwind.config.ts diff --git a/src/config-webpack-app.ts b/src/config-webpack-app.ts index dc43fdf..129b37d 100644 --- a/src/config-webpack-app.ts +++ b/src/config-webpack-app.ts @@ -9,16 +9,14 @@ import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; import TerserPlugin from 'terser-webpack-plugin'; import { generateScopedName, - generateScopedNameProduction, - getLINCDDependencies, - getLinkedTailwindColors, + generateScopedNameProduction,getLINCDDependencies, } from './utils.js'; -// import tailwindPlugin from 'tailwindcss/plugin.js'; import { LinkedFileStorage } from 'lincd/utils/LinkedFileStorage'; import postcssUrl from 'postcss-url'; //@ts-ignore import plugin from 'tailwindcss/plugin'; +// import { addLincdSourcesPlugin } from './plugins/lincd-tailwind-sources'; const isProduction = process.env.NODE_ENV === 'production'; const isDevelopment = process.env.NODE_ENV === 'development'; @@ -35,17 +33,9 @@ class WatchRunPlugin compiler.hooks.watchRun.tap('watchRun',(comp) => { if (comp.modifiedFiles) { - // const changedFiles = Array.from( - // comp.modifiedFiles, - // (file) => `\n ${file}`, - // ).join(''); - // if (changedFiles.length) - // { - // console.log(chalk.magenta('Changed files:'),changedFiles); - // } const changedFiles = Array.from(comp.modifiedFiles); - console.log(chalk.magenta('Changed files:')); + console.log(chalk.magenta('Changed files sorted by \'modified time\' stamps:')); const entriesToCheck = []; changedFiles.forEach((file) => { @@ -59,7 +49,10 @@ class WatchRunPlugin const fullPath = path.join(file.toString(), name); try { const innerStat = fs.statSync(fullPath); - entriesToCheck.push({ path: fullPath, mtime: innerStat.mtime }); + //if less than 2 minutes ago... + if (innerStat.mtime > new Date(Date.now() - 2 * 60 * 1000)) { + entriesToCheck.push({ path: fullPath, mtime: innerStat.mtime }); + } } catch (e) { entriesToCheck.push({ path: fullPath, mtime: new Date(0), error: e.message }); } @@ -72,7 +65,7 @@ class WatchRunPlugin entriesToCheck .sort((a, b) => b.mtime - a.mtime) - .splice(0,7) + .splice(0,3) .forEach((entry) => { const label = entry.error ? `[error: ${entry.error}]` @@ -84,6 +77,12 @@ class WatchRunPlugin } } +/** + * Converts a css class name to a unique scoped name (for CSS Modules) + * @param context + * @param currentFormat + * @param name + */ function getLocalIdent(context,currentFormat,name) { return isProduction ? generateScopedNameProduction(name,context.resourcePath) : generateScopedName(name,context.resourcePath); @@ -129,22 +128,25 @@ export const getLincdConfig = async () => { export const getWebpackAppConfig = async () => { - // get from the project's config-frontend file + // set up the storage config for the app await import(path.join(process.cwd(),'scripts','storage-config.js')); const accessURL = LinkedFileStorage.accessURL; - // Should relate to the use of express.static() in LincdServer.tsx, which makes the build files available through a URL + // set up the public path for the app + // This should match the use of express.static() in LincdServer.tsx, which makes the build files available through a URL const publicPath = '/public'; const bundlesPath = publicPath + '/bundles/'; - // ASSET_PATH mostly used for the apps to load the assets from the correct path + + // ASSET_PATH is used load the assets from the correct path const ASSET_PATH = process.env.ASSET_PATH || accessURL ? accessURL + bundlesPath : bundlesPath; let config = await getLincdConfig(); - let postcssPlugins = []; + let postcssPlugins = [ + ]; - //tailwind first (so its processed last and doesnt overwrite custom CSS modules) + //tailwind first (so its processed last and doesn't overwrite custom CSS modules) if (config.cssMode === 'tailwind' || config.cssMode === 'mixed') { //make sure that tailwind classes from any LINCD packages that are listed in package.json:dependencies are included @@ -158,10 +160,27 @@ export const getWebpackAppConfig = async () => { postcssPlugins.push([ '@tailwindcss/postcss', { - content: [ - (process.env.SOURCE_PATH || './src/') + '**/*.{tsx,ts}', - ...lincdPackagePaths, - ], + content: { + files:[ + (process.env.SOURCE_PATH || './src/') + '**/*.{js}', + // ...lincdPackagePaths, + ] + }, + // config: { + // content: { + // files:[ + // (process.env.SOURCE_PATH || './src/') + '**/*.{tsx,ts}', + // ...lincdPackagePaths, + // ] + // }, + // // plugins:[ + // // addLincdSourcesPlugin(), + // // ] + // }, + // content: [ + // (process.env.SOURCE_PATH || './src/') + '**/*.{tsx,ts}', + // ...lincdPackagePaths, + // ], // safelist: isProduction // ? {} // : { @@ -174,104 +193,104 @@ export const getWebpackAppConfig = async () => { // generateAll: true, // }, // }, - theme: { - extend: { - colors: getLinkedTailwindColors(), - }, - }, + // theme: { + // extend: { + // colors: getLinkedTailwindColors(), + // }, + // }, plugins: [ - plugin(function({ addBase, theme }) { + // plugin(function({ addBase, theme }) { //add styles to the base styles //this replicates the preflight settings of tailwind v4, but without the destructive/strict #/# selectors - addBase({ - // Reset all elements except common inline tags and semantic containers - '*:not(code):not(pre):not(kbd):not(samp):not(mark):not(q):not(ins):not(del):not(span):not(a):not(b):not(i):not(em):not(u):not(s):not(small):not(strong):not(sub):not(sup), ::before, ::after': { - boxSizing: 'border-box', - margin: '0', - padding: '0', - borderWidth: '0', - borderStyle: 'solid', - borderColor: 'currentColor', - }, - html: { - lineHeight: '1.5', - textSizeAdjust: '100%', - WebkitTextSizeAdjust: '100%', - MozTextSizeAdjust: '100%', - fontFamily: 'system-ui, sans-serif', - }, - body: { - margin: '0', - lineHeight: 'inherit', - backgroundColor: 'white', - }, - hr: { - height: '0', - color: 'inherit', - borderTopWidth: '1px', - }, - abbr: { - textDecoration: 'underline dotted', - }, - 'b, strong': { - fontWeight: 'bolder', - }, - 'code, kbd, samp, pre': { - fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', - fontSize: '1em', - }, - small: { - fontSize: '80%', - }, - 'sub, sup': { - fontSize: '75%', - lineHeight: '0', - position: 'relative', - verticalAlign: 'baseline', - }, - sub: { bottom: '-0.25em' }, - sup: { top: '-0.5em' }, - table: { - textIndent: '0', - borderColor: 'inherit', - borderCollapse: 'collapse', - }, - 'button, input, optgroup, select, textarea': { - font: 'inherit', - color: 'inherit', - margin: '0', - padding: '0', - lineHeight: 'inherit', - backgroundColor: 'transparent', - borderColor: 'inherit', - }, - 'button, select': { - textTransform: 'none', - }, - 'button, [type="button"], [type="reset"], [type="submit"]': { - appearance: 'button', - WebkitAppearance: 'button', - }, - '::-moz-focus-inner': { - borderStyle: 'none', - padding: '0', - }, - ':-moz-focusring': { - outline: 'auto', - }, - ':-moz-ui-invalid': { - boxShadow: 'none', - }, - fieldset: { - margin: '0', - padding: '0', - border: '0', - }, - legend: { - padding: '0', - }, - }); - }), + // addBase({ + // // Reset all elements except common inline tags and semantic containers + // '*:not(code):not(pre):not(kbd):not(samp):not(mark):not(q):not(ins):not(del):not(span):not(a):not(b):not(i):not(em):not(u):not(s):not(small):not(strong):not(sub):not(sup), ::before, ::after': { + // boxSizing: 'border-box', + // margin: '0', + // padding: '0', + // borderWidth: '0', + // borderStyle: 'solid', + // borderColor: 'currentColor', + // }, + // html: { + // lineHeight: '1.5', + // textSizeAdjust: '100%', + // WebkitTextSizeAdjust: '100%', + // MozTextSizeAdjust: '100%', + // fontFamily: 'system-ui, sans-serif', + // }, + // body: { + // margin: '0', + // lineHeight: 'inherit', + // backgroundColor: 'white', + // }, + // hr: { + // height: '0', + // color: 'inherit', + // borderTopWidth: '1px', + // }, + // abbr: { + // textDecoration: 'underline dotted', + // }, + // 'b, strong': { + // fontWeight: 'bolder', + // }, + // 'code, kbd, samp, pre': { + // fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', + // fontSize: '1em', + // }, + // small: { + // fontSize: '80%', + // }, + // 'sub, sup': { + // fontSize: '75%', + // lineHeight: '0', + // position: 'relative', + // verticalAlign: 'baseline', + // }, + // sub: { bottom: '-0.25em' }, + // sup: { top: '-0.5em' }, + // table: { + // textIndent: '0', + // borderColor: 'inherit', + // borderCollapse: 'collapse', + // }, + // 'button, input, optgroup, select, textarea': { + // font: 'inherit', + // color: 'inherit', + // margin: '0', + // padding: '0', + // lineHeight: 'inherit', + // backgroundColor: 'transparent', + // borderColor: 'inherit', + // }, + // 'button, select': { + // textTransform: 'none', + // }, + // 'button, [type="button"], [type="reset"], [type="submit"]': { + // appearance: 'button', + // WebkitAppearance: 'button', + // }, + // '::-moz-focus-inner': { + // borderStyle: 'none', + // padding: '0', + // }, + // ':-moz-focusring': { + // outline: 'auto', + // }, + // ':-moz-ui-invalid': { + // boxShadow: 'none', + // }, + // fieldset: { + // margin: '0', + // padding: '0', + // border: '0', + // }, + // legend: { + // padding: '0', + // }, + // }); + // }), ], // plugins: [ // tailwindPlugin(function({ addBase,config }) { @@ -285,11 +304,20 @@ export const getWebpackAppConfig = async () => { // ], }, ]); + } else { + //if not using tailwind, then we use postcss-nested to enable nesting of css + postcssPlugins.push( + ['postcss-import',{}], + ['postcss-preset-env',{ + features: { 'nesting-rules': true }, + }] + ); + } + //Add plugin which converts URLs in CSS to the correct FULL absolute path postcssPlugins.push([ postcssUrl({ url: (asset) => { - //This convert URLs used in CSS and adds the full public URL instead of a local path //TODO: for assets of packages, we want to detect the package path and resolve through node_modules/package-name/...something/assets if(!asset.url.startsWith('data:')) { @@ -336,24 +364,9 @@ export const getWebpackAppConfig = async () => { // 'number-max-precision':null, // }, // }], - 'postcss-preset-env', isProduction && 'cssnano', // "postcss-reporter", ]); - if (config.cssMode === 'scss-modules' || config.cssMode === 'mixed') - { - //NOTE: this is replaced with the css-loader - // postcssPlugins.push([ - // 'postcss-modules', - // { - // generateScopedName: generateScopedName, - // globalModulePaths: (Array.isArray(config.cssGlobalModulePaths) - // ? [/tailwind/, ...config.cssGlobalModulePaths] - // : [/tailwind/, config.cssGlobalModulePaths] - // ).filter(Boolean), - // }, - // ]); - } } return { @@ -390,13 +403,11 @@ export const getWebpackAppConfig = async () => { }, }, plugins: [ - new WatchRunPlugin(), - // new RebuildScssJsonPlugin(), + // new WatchRunPlugin(), new MiniCssExtractPlugin({ ignoreOrder:true }), new webpack.EnvironmentPlugin(Object.keys(process.env)), - // new ForkTsCheckerWebpackPlugin(), isDevelopment && new ReactRefreshWebpackPlugin(), isDevelopment && new webpack.HotModuleReplacementPlugin(), config.analyse && new BundleAnalyzerPlugin(), @@ -416,21 +427,11 @@ export const getWebpackAppConfig = async () => { importLoaders: 1, modules: { mode: 'local', - // namedExport:true, - // exportOnlyLocals:true, - // exclude:/tailwind/i, getLocalIdent: getLocalIdent, - // auto: (resourcePath: string) => { - // // Use CSS Modules for everything except node_modules/tailwind - // return !/node_modules[\\/]tailwind/.test(resourcePath); - // }, auto: (resourcePath: string) => { //make sure this only applies to .module.css files, and not to tailwind return /\.module\.css$/i.test(resourcePath) && !/tailwind/i.test(resourcePath); } - // localIdentName: '[local]--[hash:base64:6]', - // localIdentName: '[local]', - // localIdentName: "[path][name]__[local]--[hash:base64:5]", }, }, @@ -443,31 +444,8 @@ export const getWebpackAppConfig = async () => { }, }, }, - // { - // loader: 'sass-loader', - // options: { sourceMap: true }, - // }, ], }, - // { - // test: /\.(ts|tsx)$/, - // exclude: /node_modules/, - // include: [path.join(process.cwd(),"frontend")], // only bundle files in this directory - // use: { - // loader: "babel-loader", // cf. .babelrc.json in this folder and browser list in package.json - // options: { - // plugins: isDevelopment ? ["react-refresh/babel"] : [], - // cacheCompression: false, - // cacheDirectory: true, - // }, - // }, - // }, - // { - // test: /\.m?js/, - // resolve: { - // fullySpecified: false - // } - // }, { test: /\.tsx?$/, use: [ diff --git a/src/index.ts b/src/index.ts index 1e91b88..2786ebf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export {default as DeclarationPlugin} from './plugins/declaration-plugin'; export {default as externaliseModules} from './plugins/externalise-modules'; export {default as checkImports} from './plugins/check-imports'; +export {default as tailwindConfig} from './tailwind.config'; // export {default as generateGruntConfig} from './config-grunt'; // export {buildMetadata} from './metadata'; import {generateWebpackConfig} from './config-webpack'; diff --git a/src/plugins/lincd-tailwind-sources.ts b/src/plugins/lincd-tailwind-sources.ts new file mode 100644 index 0000000..198857e --- /dev/null +++ b/src/plugins/lincd-tailwind-sources.ts @@ -0,0 +1,39 @@ +//@ts-ignore +import plugin from 'tailwindcss/plugin'; +import path from 'path'; +import fs from 'fs'; +import { + getLINCDDependencies, +} from '../utils.js'; + + +const getLincdSources = () => { + // return plugin.withOptions( + // () => {}, + // () => { + const sources: string[] = []; + + // Always include your app source + // sources.push('./src/**/*.{js,ts,tsx}'); + + // Now dynamically add LINCD packages + const packageJson = JSON.parse( + fs.readFileSync(path.resolve(process.cwd(), 'package.json'), 'utf-8') + ); + + const lincdDeps = getLINCDDependencies(packageJson); + lincdDeps.forEach(([name, packagePath]) => { + sources.push(`${packagePath}/lib/**/*.{js,ts,tsx,mjs}`); + }); + + // console.log('sources:', sources.join("\n")); + + return sources; + // return {}; + // return { + // // Extend Tailwind's `content` config + // content: sources, + // }; + // }); +} +export default getLincdSources; \ No newline at end of file diff --git a/src/tailwind.config.ts b/src/tailwind.config.ts new file mode 100644 index 0000000..2a9c19f --- /dev/null +++ b/src/tailwind.config.ts @@ -0,0 +1,13 @@ +import getLincdSources from './plugins/lincd-tailwind-sources'; +// console.log('✅ Loaded tailwind.config.js'); +const config = { + content: [ + './src/**/*.{js,ts,tsx}', + ...getLincdSources(), + ], + theme: { + extend: {}, + }, +}; + +export default config; \ No newline at end of file From c6429e21a0ec2e4c5b7f999a130d8c772a4e31de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81=20Verheij?= Date: Fri, 9 May 2025 09:33:20 +0100 Subject: [PATCH 21/87] published new version with updated defaults for LINCD 1.0 --- defaults/app-with-backend/.eslintignore | 4 +- defaults/app-with-backend/babel.config.js | 4 - defaults/app-with-backend/lincd.config.js | 10 +- defaults/app-with-backend/package.json | 21 ++-- .../{web => public}/favicon-144x144.png | Bin .../{web => public}/favicon-57x57.png | Bin .../{web => public}/favicon-72x72.png | Bin .../{web => public}/favicon.ico | Bin .../scripts/storage-config.js | 10 +- defaults/app-with-backend/src/App.module.css | 17 +++ defaults/app-with-backend/src/App.scss | 6 -- defaults/app-with-backend/src/App.scss.json | 1 - defaults/app-with-backend/src/App.tsx | 26 ++--- defaults/app-with-backend/src/backend.ts | 8 ++ .../{Error.scss => Error.module.css} | 0 .../src/components/Error.scss.json | 1 - .../app-with-backend/src/components/Error.tsx | 7 +- .../{Spinner.scss => Spinner.module.css} | 0 .../src/components/Spinner.scss.json | 1 - .../src/components/Spinner.tsx | 6 +- .../app-with-backend/src/config-frontend.ts | 12 +-- defaults/app-with-backend/src/index.tsx | 1 + .../src/layout/DefaultLayout.module.css | 12 +++ .../src/layout/DefaultLayout.scss | 6 -- .../src/layout/DefaultLayout.scss.json | 1 - .../src/layout/DefaultLayout.tsx | 7 +- .../src/layout/Header.module.css | 25 +++++ .../app-with-backend/src/layout/Header.scss | 10 -- .../src/layout/Header.scss.json | 1 - .../app-with-backend/src/layout/Header.tsx | 16 +-- defaults/app-with-backend/src/package.ts | 1 - .../src/pages/{Home.scss => Home.module.css} | 0 .../app-with-backend/src/pages/Home.scss.json | 1 - defaults/app-with-backend/src/pages/Home.tsx | 5 +- defaults/app-with-backend/src/routes.tsx | 15 +-- .../src/scss/global-overwrites.scss | 11 -- .../app-with-backend/src/scss/variables.scss | 23 ---- defaults/app-with-backend/src/theme.css | 100 ++++++++++++++++++ defaults/app-with-backend/tsconfig.json | 12 ++- package.json | 2 +- src/cli-methods.ts | 38 ++++--- 41 files changed, 257 insertions(+), 164 deletions(-) delete mode 100644 defaults/app-with-backend/babel.config.js rename defaults/app-with-backend/{web => public}/favicon-144x144.png (100%) rename defaults/app-with-backend/{web => public}/favicon-57x57.png (100%) rename defaults/app-with-backend/{web => public}/favicon-72x72.png (100%) rename defaults/app-with-backend/{web => public}/favicon.ico (100%) create mode 100644 defaults/app-with-backend/src/App.module.css delete mode 100644 defaults/app-with-backend/src/App.scss delete mode 100644 defaults/app-with-backend/src/App.scss.json rename defaults/app-with-backend/src/components/{Error.scss => Error.module.css} (100%) delete mode 100644 defaults/app-with-backend/src/components/Error.scss.json rename defaults/app-with-backend/src/components/{Spinner.scss => Spinner.module.css} (100%) delete mode 100644 defaults/app-with-backend/src/components/Spinner.scss.json create mode 100644 defaults/app-with-backend/src/layout/DefaultLayout.module.css delete mode 100644 defaults/app-with-backend/src/layout/DefaultLayout.scss delete mode 100644 defaults/app-with-backend/src/layout/DefaultLayout.scss.json create mode 100644 defaults/app-with-backend/src/layout/Header.module.css delete mode 100644 defaults/app-with-backend/src/layout/Header.scss delete mode 100644 defaults/app-with-backend/src/layout/Header.scss.json rename defaults/app-with-backend/src/pages/{Home.scss => Home.module.css} (100%) delete mode 100644 defaults/app-with-backend/src/pages/Home.scss.json delete mode 100644 defaults/app-with-backend/src/scss/global-overwrites.scss delete mode 100644 defaults/app-with-backend/src/scss/variables.scss create mode 100644 defaults/app-with-backend/src/theme.css diff --git a/defaults/app-with-backend/.eslintignore b/defaults/app-with-backend/.eslintignore index c65a39b..4ec8b89 100644 --- a/defaults/app-with-backend/.eslintignore +++ b/defaults/app-with-backend/.eslintignore @@ -1,3 +1,5 @@ node_modules public -build \ No newline at end of file +build +data +web \ No newline at end of file diff --git a/defaults/app-with-backend/babel.config.js b/defaults/app-with-backend/babel.config.js deleted file mode 100644 index 3dc0d1c..0000000 --- a/defaults/app-with-backend/babel.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - presets: ['@babel/preset-env', ['@babel/preset-react', {runtime: 'automatic'}], '@babel/preset-typescript'], - plugins: ['@babel/plugin-transform-runtime',['@babel/plugin-proposal-decorators',{legacy:true}]], -}; diff --git a/defaults/app-with-backend/lincd.config.js b/defaults/app-with-backend/lincd.config.js index 3ef86e6..80476b1 100644 --- a/defaults/app-with-backend/lincd.config.js +++ b/defaults/app-with-backend/lincd.config.js @@ -1,7 +1,5 @@ -module.exports = { - cssMode: 'scss-modules', - cacheWebpack:true, - cssGlobalModulePaths:[/global-overwrites.scss/], - analyse:false, - +export default { + cssMode: 'mixed', + cacheWebpack: true, + analyse: false, }; diff --git a/defaults/app-with-backend/package.json b/defaults/app-with-backend/package.json index 6454541..3f2750d 100644 --- a/defaults/app-with-backend/package.json +++ b/defaults/app-with-backend/package.json @@ -4,6 +4,7 @@ "description": "", "version": "0.1.0", "private": true, + "type": "module", "lincdApp": true, "author": { "name": "", @@ -18,7 +19,7 @@ "scripts": { "start": "node --import ./node_modules/lincd-cli/lib/esm/loaders/register.js node_modules/.bin/lincd-cli start --env development", "build": "yarn lincd build-app --env production", - "postinstall": "husky install", + "prepare": "husky", "prepack": "pinst --disable", "postpack": "pinst --enable", "server:prod": "yarn lincd start --env production", @@ -36,7 +37,7 @@ "prettier --write" ] }, - "workspaces" : [ + "workspaces": [ "packages/*" ], "keywords": [ @@ -49,21 +50,10 @@ "web3" ], "dependencies": { - "@babel/cli": "^7.22.9", - "@babel/core": "^7.22.9", - "@babel/plugin-proposal-decorators": "^7.22.7", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.5", - "@babel/register": "^7.22.5", - "chalk": "^4.1.2", - "lincd": "^1.0", + "lincd": "~1.0", "lincd-auth": "~1.0", - "lincd-jsonld": "~1.0", "lincd-server": "~1.0", "lincd-server-utils": "~1.0", - "node-gyp": "^11.1.0", "react": "^18.2", "react-dom": "^18.2", "react-error-boundary": "^3.1.3", @@ -83,10 +73,11 @@ "eslint-plugin-react": "latest", "eslint-plugin-react-hooks": "^4.6.0", "husky": "^8.0.0", - "lincd-cli": "^1.1", + "lincd-cli": "^1.2.1", "nodemon": "^2.0.22", "pinst": "^3.0.0", "pm2": "^5.3.0", + "typescript-plugin-css-modules": "^5.1.0", "which": "^3.0.1" }, "packageManager": "yarn@3.6.1" diff --git a/defaults/app-with-backend/web/favicon-144x144.png b/defaults/app-with-backend/public/favicon-144x144.png similarity index 100% rename from defaults/app-with-backend/web/favicon-144x144.png rename to defaults/app-with-backend/public/favicon-144x144.png diff --git a/defaults/app-with-backend/web/favicon-57x57.png b/defaults/app-with-backend/public/favicon-57x57.png similarity index 100% rename from defaults/app-with-backend/web/favicon-57x57.png rename to defaults/app-with-backend/public/favicon-57x57.png diff --git a/defaults/app-with-backend/web/favicon-72x72.png b/defaults/app-with-backend/public/favicon-72x72.png similarity index 100% rename from defaults/app-with-backend/web/favicon-72x72.png rename to defaults/app-with-backend/public/favicon-72x72.png diff --git a/defaults/app-with-backend/web/favicon.ico b/defaults/app-with-backend/public/favicon.ico similarity index 100% rename from defaults/app-with-backend/web/favicon.ico rename to defaults/app-with-backend/public/favicon.ico diff --git a/defaults/app-with-backend/scripts/storage-config.js b/defaults/app-with-backend/scripts/storage-config.js index 116e9a7..872b0ca 100644 --- a/defaults/app-with-backend/scripts/storage-config.js +++ b/defaults/app-with-backend/scripts/storage-config.js @@ -1,11 +1,11 @@ -const { Storage } = require('lincd/lib/utils/Storage'); -const { LocalFileStore } = require('lincd-server/lib/shapes/filestores/LocalFileStore'); -const { LinkedFileStorage } = require('lincd/lib/utils/LinkedFileStorage'); -const { N3FileStore } = require('lincd-server/lib/shapes/quadstores/N3FileStore'); +import { LinkedStorage } from 'lincd/utils/LinkedStorage'; +import { LinkedFileStorage } from 'lincd/utils/LinkedFileStorage'; +import { LocalFileStore } from 'lincd-server/shapes/filestores/LocalFileStore'; +import { N3FileStore } from 'lincd-server/shapes/quadstores/N3FileStore'; //How quads are stored let quadStore = new N3FileStore(process.env.NODE_ENV + '-main'); -Storage.setDefaultStore(quadStore); +LinkedStorage.setDefaultStore(quadStore); //How files are stored let fileStore = new LocalFileStore(process.env.NODE_ENV + '-main'); diff --git a/defaults/app-with-backend/src/App.module.css b/defaults/app-with-backend/src/App.module.css new file mode 100644 index 0000000..7f62ebc --- /dev/null +++ b/defaults/app-with-backend/src/App.module.css @@ -0,0 +1,17 @@ +.App { + display: block; + color: #141414; + margin-top: 1.5rem; +} +body { + font-family: 'Roboto', sans-serif; + background-color: #ffffff; +} +h1 { + font-size: 2rem; + font-weight: 100; +} +h2 { + font-size: 1.5em; + font-weight: bold; +} diff --git a/defaults/app-with-backend/src/App.scss b/defaults/app-with-backend/src/App.scss deleted file mode 100644 index 73c0189..0000000 --- a/defaults/app-with-backend/src/App.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import 'scss/variables.scss'; - -.App { - display: block; - color: $textColor; -} diff --git a/defaults/app-with-backend/src/App.scss.json b/defaults/app-with-backend/src/App.scss.json deleted file mode 100644 index c0443c6..0000000 --- a/defaults/app-with-backend/src/App.scss.json +++ /dev/null @@ -1 +0,0 @@ -{"App":"${underscore_name}_App_App","error":"${underscore_name}_App_error","content":"${underscore_name}_App_content","main":"${underscore_name}_App_main","header":"${underscore_name}_App_header","menu":"${underscore_name}_App_menu"} \ No newline at end of file diff --git a/defaults/app-with-backend/src/App.tsx b/defaults/app-with-backend/src/App.tsx index cd93a04..0535593 100644 --- a/defaults/app-with-backend/src/App.tsx +++ b/defaults/app-with-backend/src/App.tsx @@ -1,18 +1,14 @@ -import React, {Suspense} from 'react'; -import {ErrorBoundary} from 'react-error-boundary'; -import {Spinner} from './components/Spinner'; -import {Error} from './components/Error'; -import {AppRoot} from 'lincd-server-utils/lib/components/AppRoot'; -import {Head} from 'lincd-server-utils/lib/components/Head'; -import {Body} from 'lincd-server-utils/lib/components/Body'; -import AppRoutes, {ROUTES} from './routes'; -import {ProvideAuth} from 'lincd-auth/lib/hooks/useAuth'; - -//Note that by default LINCD apps are set up with support for SCSS (sass) and CSS Modules -//So any .scss file needs to be imported by itself -import './App.scss'; -//and then the .scss.json file needs to be imported to access the class names (this file will be automatically generated) -import style from './App.scss.json'; +import './theme.css'; //needs to be the first import before importing other components +import React, { Suspense } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; +import { Spinner } from './components/Spinner'; +import { Error } from './components/Error'; +import { AppRoot } from 'lincd-server-utils/components/AppRoot'; +import { Head } from 'lincd-server-utils/components/Head'; +import { Body } from 'lincd-server-utils/components/Body'; +import AppRoutes, { ROUTES } from './routes'; +import { ProvideAuth } from 'lincd-auth/hooks/useAuth'; +import style from './App.module.css'; //import any .module.css file like this and access the classnames from the style object export default function App() { return ( diff --git a/defaults/app-with-backend/src/backend.ts b/defaults/app-with-backend/src/backend.ts index e0b6ff7..ac1cf99 100644 --- a/defaults/app-with-backend/src/backend.ts +++ b/defaults/app-with-backend/src/backend.ts @@ -1 +1,9 @@ //import all ShapeProviders here or define a generic BackendProvider, see documentation on https://docs.lincd.org + +import { BackendProvider } from 'lincd-server-utils/utils/BackendProvider'; + +export class Backend extends BackendProvider { + constructor(server) { + super(server); + } +} diff --git a/defaults/app-with-backend/src/components/Error.scss b/defaults/app-with-backend/src/components/Error.module.css similarity index 100% rename from defaults/app-with-backend/src/components/Error.scss rename to defaults/app-with-backend/src/components/Error.module.css diff --git a/defaults/app-with-backend/src/components/Error.scss.json b/defaults/app-with-backend/src/components/Error.scss.json deleted file mode 100644 index 6db4c4a..0000000 --- a/defaults/app-with-backend/src/components/Error.scss.json +++ /dev/null @@ -1 +0,0 @@ -{"error":"${underscore_name}_Error_error"} \ No newline at end of file diff --git a/defaults/app-with-backend/src/components/Error.tsx b/defaults/app-with-backend/src/components/Error.tsx index cb9444e..8595c93 100644 --- a/defaults/app-with-backend/src/components/Error.tsx +++ b/defaults/app-with-backend/src/components/Error.tsx @@ -1,8 +1,7 @@ import React from 'react'; -import style from './Error.scss.json'; -import './Error.scss'; +import style from './Error.module.css'; -export function Error({error}) { +export function Error({ error }) { return (

Application Error

@@ -10,5 +9,3 @@ export function Error({error}) {
); } - - diff --git a/defaults/app-with-backend/src/components/Spinner.scss b/defaults/app-with-backend/src/components/Spinner.module.css similarity index 100% rename from defaults/app-with-backend/src/components/Spinner.scss rename to defaults/app-with-backend/src/components/Spinner.module.css diff --git a/defaults/app-with-backend/src/components/Spinner.scss.json b/defaults/app-with-backend/src/components/Spinner.scss.json deleted file mode 100644 index ecfaf27..0000000 --- a/defaults/app-with-backend/src/components/Spinner.scss.json +++ /dev/null @@ -1 +0,0 @@ -{"spinner":"${underscore_name}_Spinner_spinner","active":"${underscore_name}_Spinner_active","spin":"${underscore_name}_Spinner_spin"} \ No newline at end of file diff --git a/defaults/app-with-backend/src/components/Spinner.tsx b/defaults/app-with-backend/src/components/Spinner.tsx index 2db605b..6fbc065 100644 --- a/defaults/app-with-backend/src/components/Spinner.tsx +++ b/defaults/app-with-backend/src/components/Spinner.tsx @@ -1,7 +1,7 @@ -import './Spinner.scss'; -import * as style from './Spinner.scss.json'; +import React from 'react'; +import style from './Spinner.module.css'; -export function Spinner({active = true}) { +export function Spinner({ active = true }) { return (
{}; window['$RefreshSig$'] = () => () => {}; diff --git a/defaults/app-with-backend/src/layout/DefaultLayout.module.css b/defaults/app-with-backend/src/layout/DefaultLayout.module.css new file mode 100644 index 0000000..536e36c --- /dev/null +++ b/defaults/app-with-backend/src/layout/DefaultLayout.module.css @@ -0,0 +1,12 @@ +.main { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, + Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + margin: auto; + width: min(66.6%, 1000px); + @media (max-width: 1024px) { + width: 80%; + } + @media (max-width: 768px) { + width: 90%; + } +} diff --git a/defaults/app-with-backend/src/layout/DefaultLayout.scss b/defaults/app-with-backend/src/layout/DefaultLayout.scss deleted file mode 100644 index ff25790..0000000 --- a/defaults/app-with-backend/src/layout/DefaultLayout.scss +++ /dev/null @@ -1,6 +0,0 @@ -.main { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, - Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; - width: 66.6%; - margin: auto; -} diff --git a/defaults/app-with-backend/src/layout/DefaultLayout.scss.json b/defaults/app-with-backend/src/layout/DefaultLayout.scss.json deleted file mode 100644 index 406dccc..0000000 --- a/defaults/app-with-backend/src/layout/DefaultLayout.scss.json +++ /dev/null @@ -1 +0,0 @@ -{"main":"${underscore_name}_DefaultLayout_main"} \ No newline at end of file diff --git a/defaults/app-with-backend/src/layout/DefaultLayout.tsx b/defaults/app-with-backend/src/layout/DefaultLayout.tsx index 57700df..49f42ac 100644 --- a/defaults/app-with-backend/src/layout/DefaultLayout.tsx +++ b/defaults/app-with-backend/src/layout/DefaultLayout.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import {Header} from './Header'; -import './DefaultLayout.scss'; -import style from './DefaultLayout.scss.json'; +import { Header } from './Header'; +import style from './DefaultLayout.module.css'; -export function DefaultLayout({children}) { +export function DefaultLayout({ children }) { return (
diff --git a/defaults/app-with-backend/src/layout/Header.module.css b/defaults/app-with-backend/src/layout/Header.module.css new file mode 100644 index 0000000..c5c8ea6 --- /dev/null +++ b/defaults/app-with-backend/src/layout/Header.module.css @@ -0,0 +1,25 @@ +.header { + display: block; +} + +.menu { + margin: 1rem 0 3rem; + display: grid; + grid-template-columns: repeat(6, 1fr); + grid-gap: 1rem; + > a { + text-align: center; + font-weight: bold; + font-size: 1.2rem; + background: black; + color: white; + padding: 5px 10px; + text-decoration: none; + &.active { + background: white; + color: black; + box-shadow: 1px 8px 10px #888; + border: 1px solid #444; + } + } +} diff --git a/defaults/app-with-backend/src/layout/Header.scss b/defaults/app-with-backend/src/layout/Header.scss deleted file mode 100644 index 66f78b3..0000000 --- a/defaults/app-with-backend/src/layout/Header.scss +++ /dev/null @@ -1,10 +0,0 @@ -.header { - display: block; -} - -.menu { - margin: 1rem 0; - display: grid; - grid-template-columns: repeat(6, 1fr); - grid-gap: 1rem; -} diff --git a/defaults/app-with-backend/src/layout/Header.scss.json b/defaults/app-with-backend/src/layout/Header.scss.json deleted file mode 100644 index 7eafeec..0000000 --- a/defaults/app-with-backend/src/layout/Header.scss.json +++ /dev/null @@ -1 +0,0 @@ -{"header":"${underscore_name}_Header_header","menu":"${underscore_name}_Header_menu"} \ No newline at end of file diff --git a/defaults/app-with-backend/src/layout/Header.tsx b/defaults/app-with-backend/src/layout/Header.tsx index 974e920..e6254d2 100644 --- a/defaults/app-with-backend/src/layout/Header.tsx +++ b/defaults/app-with-backend/src/layout/Header.tsx @@ -1,18 +1,22 @@ -import {Link} from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import React from 'react'; -import style from './Header.scss.json'; -import './Header.scss'; -import {ROUTES} from '../routes'; +import style from './Header.module.css'; +import { ROUTES } from '../routes'; export function Header() { + let path = useLocation(); return (
-

${name}

+

LINCD 1.0 Demo App