From 1f65804e5156e376f9984cf65b2e8b620bbd4ba3 Mon Sep 17 00:00:00 2001 From: Myyk Seok <2080820+myyk@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:37:09 +0800 Subject: [PATCH 1/5] feat: improve TypeScriptModule --- .gitignore | 1 + .../javascriptlib/basic/1-simple/.gitignore | 144 ++++++++++++++++++ .../javascriptlib/basic/1-simple/build.mill | 2 +- .../mill/javascriptlib/TypeScriptModule.scala | 67 ++++++-- 4 files changed, 197 insertions(+), 17 deletions(-) create mode 100644 example/javascriptlib/basic/1-simple/.gitignore diff --git a/.gitignore b/.gitignore index 3bb56edddb3..11df1ea72a3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ mill.iml bsp.log lowered.hnir .dotty-ide* +node_modules/ \ No newline at end of file diff --git a/example/javascriptlib/basic/1-simple/.gitignore b/example/javascriptlib/basic/1-simple/.gitignore new file mode 100644 index 00000000000..3502ef7fa26 --- /dev/null +++ b/example/javascriptlib/basic/1-simple/.gitignore @@ -0,0 +1,144 @@ +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +# End of https://www.toptal.com/developers/gitignore/api/node diff --git a/example/javascriptlib/basic/1-simple/build.mill b/example/javascriptlib/basic/1-simple/build.mill index ea085de4f02..06f9196915d 100644 --- a/example/javascriptlib/basic/1-simple/build.mill +++ b/example/javascriptlib/basic/1-simple/build.mill @@ -3,7 +3,7 @@ import mill._, javascriptlib._ object foo extends TypeScriptModule { object bar extends TypeScriptModule { - def npmDeps = Seq("immutable@4.3.7") + def npmDeps = Seq("immutable@4.3.7") } } diff --git a/javascriptlib/src/mill/javascriptlib/TypeScriptModule.scala b/javascriptlib/src/mill/javascriptlib/TypeScriptModule.scala index d9c2cf4f09d..025283c312d 100644 --- a/javascriptlib/src/mill/javascriptlib/TypeScriptModule.scala +++ b/javascriptlib/src/mill/javascriptlib/TypeScriptModule.scala @@ -6,28 +6,63 @@ trait TypeScriptModule extends Module { def npmDeps: T[Seq[String]] = Task { Seq.empty[String] } + def npmDevDeps: T[Seq[String]] = Task { Seq( + "typescript@5.6.3", + "@types/node@22.7.8", + "esbuild@0.24.0" + ) } + def transitiveNpmDeps: T[Seq[String]] = Task { - Task.traverse(moduleDeps)(_.npmDeps)().flatten ++ npmDeps() + Task.traverse(moduleDeps)(_.npmDeps)().flatten + } + + def transitiveNpmDevDeps: T[Seq[String]] = Task { + Task.traverse(moduleDeps)(_.npmDevDeps)().flatten + } + + def install = Task { + PathRef(nodeModulesInModuleRoot().path) } def npmInstall = Task { - os.call(( - "npm", - "install", - "--save-dev", - "typescript@5.6.3", - "@types/node@22.7.8", - "esbuild@0.24.0", - transitiveNpmDeps() - )) + val deps = npmDeps() ++ transitiveNpmDeps() + if (deps.nonEmpty) { + os.call(( + "npm", + "install", + deps + )) + } + val devDeps = npmDevDeps() ++ transitiveNpmDevDeps() + if (devDeps.nonEmpty) { + os.call(( + "npm", + "install", + "--save-dev", + devDeps + )) + } + PathRef(Task.dest) } + def nodeModulesInModuleRoot = Task { + val installPath = npmInstall().path + + // Copy the node_modules to the source path so that IDE can find the types. Other tasks are able to depend on this. + // Remove the old node_modules because this is a common thing to do in the JS world, also if the user did it manually, it won't repopulate if the task is cached. + os.remove.all(millSourcePath / "node_modules") + os.copy(installPath / "node_modules", millSourcePath / "node_modules", followLinks = false) + PathRef(installPath) + } + def sources = Task.Source(millSourcePath / "src") - def allSources = Task { os.walk(sources().path).filter(_.ext == "ts").map(PathRef(_)) } + def allSources = Task { os.walk(sources().path).filter(file => file.ext == "ts" || file.ext == "tsx" || file.ext == "d.ts").map(PathRef(_)) } + + def tscArgs = Task { Seq.empty[String] } def compile: T[(PathRef, PathRef)] = Task { - val nodeTypes = npmInstall().path / "node_modules/@types" + val nodeTypes = install().path / "node_modules/@types" val javascriptOut = Task.dest / "javascript" val declarationsOut = Task.dest / "declarations" @@ -35,7 +70,7 @@ trait TypeScriptModule extends Module { for (((jsDir, dTsDir), mod) <- Task.traverse(moduleDeps)(_.compile)().zip(moduleDeps)) yield (mod.millSourcePath.subRelativeTo(Task.workspace).toString + "/*", dTsDir.path) - val allPaths = upstreamPaths ++ Seq("*" -> sources().path, "*" -> npmInstall().path) + val allPaths = upstreamPaths ++ Seq("*" -> sources().path, "*" -> install().path) os.write( Task.dest / "tsconfig.json", @@ -51,7 +86,7 @@ trait TypeScriptModule extends Module { ) ) - os.call((npmInstall().path / "node_modules/typescript/bin/tsc")) + os.call((install().path / "node_modules/typescript/bin/tsc", tscArgs())) (PathRef(javascriptOut), PathRef(declarationsOut)) } @@ -64,7 +99,7 @@ trait TypeScriptModule extends Module { os.copy(jsDir.path, Task.dest / mod.millSourcePath.subRelativeTo(Task.workspace)) } val mainFile = compile()._1.path / mainFileName() - val env = Map("NODE_PATH" -> Seq(".", compile()._1.path, npmInstall().path).mkString(":")) + val env = Map("NODE_PATH" -> Seq(".", compile()._1.path, install().path).mkString(":")) (mainFile, env) } @@ -75,7 +110,7 @@ trait TypeScriptModule extends Module { def bundle = Task { val (mainFile, env) = prepareRun() - val esbuild = npmInstall().path / "node_modules/esbuild/bin/esbuild" + val esbuild = install().path / "node_modules/esbuild/bin/esbuild" val bundle = Task.dest / "bundle.js" os.call((esbuild, mainFile, "--bundle", s"--outfile=$bundle"), env = env) PathRef(bundle) From f0cb0331c1ec7176588e39d01c933dc521fd2b87 Mon Sep 17 00:00:00 2001 From: Myyk Seok <2080820+myyk@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:10:44 +0800 Subject: [PATCH 2/5] feat: split node and tsc traits in TypeScriptModule --- .../javascriptlib/basic/1-simple/.gitignore | 145 +----------------- .../src/mill/javascriptlib/NodeModule.scala | 55 +++++++ ...TypeScriptModule.scala => TscModule.scala} | 60 +------- .../mill/javascriptlib/HelloWorldTests.scala | 6 +- 4 files changed, 66 insertions(+), 200 deletions(-) create mode 100644 javascriptlib/src/mill/javascriptlib/NodeModule.scala rename javascriptlib/src/mill/javascriptlib/{TypeScriptModule.scala => TscModule.scala} (56%) diff --git a/example/javascriptlib/basic/1-simple/.gitignore b/example/javascriptlib/basic/1-simple/.gitignore index 3502ef7fa26..54b06f2c899 100644 --- a/example/javascriptlib/basic/1-simple/.gitignore +++ b/example/javascriptlib/basic/1-simple/.gitignore @@ -1,144 +1 @@ -# Created by https://www.toptal.com/developers/gitignore/api/node -# Edit at https://www.toptal.com/developers/gitignore?templates=node - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -### Node Patch ### -# Serverless Webpack directories -.webpack/ - -# Optional stylelint cache - -# SvelteKit build / generate output -.svelte-kit - -# End of https://www.toptal.com/developers/gitignore/api/node +node_module/ \ No newline at end of file diff --git a/javascriptlib/src/mill/javascriptlib/NodeModule.scala b/javascriptlib/src/mill/javascriptlib/NodeModule.scala new file mode 100644 index 00000000000..66a6864fc9a --- /dev/null +++ b/javascriptlib/src/mill/javascriptlib/NodeModule.scala @@ -0,0 +1,55 @@ +package mill.javascriptlib + +import mill.{Module, PathRef, T, Task} + +trait NodeModule extends Module { + def moduleDeps: Seq[NodeModule] = Nil + + def npmDeps: T[Seq[String]] = Task { Seq.empty[String] } + + def npmDevDeps: T[Seq[String]] = Task { Seq.empty[String] } + + def transitiveNpmDeps: T[Seq[String]] = Task { + Task.traverse(moduleDeps)(_.npmDeps)().flatten + } + + def transitiveNpmDevDeps: T[Seq[String]] = Task { + Task.traverse(moduleDeps)(_.npmDevDeps)().flatten + } + + def install = Task { + PathRef(nodeModulesInModuleRoot().path) + } + + def npmInstall = Task { + val deps = npmDeps() ++ transitiveNpmDeps() + if (deps.nonEmpty) { + os.call(( + "npm", + "install", + deps + )) + } + val devDeps = npmDevDeps() ++ transitiveNpmDevDeps() + if (devDeps.nonEmpty) { + os.call(( + "npm", + "install", + "--save-dev", + devDeps + )) + } + + PathRef(Task.dest) + } + + def nodeModulesInModuleRoot = Task { + val installPath = npmInstall().path + + // Copy the node_modules to the source path so that IDE can find the types. Other tasks are able to depend on this. + // Remove the old node_modules because this is a common thing to do in the JS world, also if the user did it manually, it won't repopulate if the task is cached. + os.remove.all(millSourcePath / "node_modules") + os.copy(installPath / "node_modules", millSourcePath / "node_modules", followLinks = false) + PathRef(installPath) + } +} diff --git a/javascriptlib/src/mill/javascriptlib/TypeScriptModule.scala b/javascriptlib/src/mill/javascriptlib/TscModule.scala similarity index 56% rename from javascriptlib/src/mill/javascriptlib/TypeScriptModule.scala rename to javascriptlib/src/mill/javascriptlib/TscModule.scala index 025283c312d..c3e395c436e 100644 --- a/javascriptlib/src/mill/javascriptlib/TypeScriptModule.scala +++ b/javascriptlib/src/mill/javascriptlib/TscModule.scala @@ -1,73 +1,27 @@ package mill.javascriptlib import mill._ -trait TypeScriptModule extends Module { - def moduleDeps: Seq[TypeScriptModule] = Nil - - def npmDeps: T[Seq[String]] = Task { Seq.empty[String] } - - def npmDevDeps: T[Seq[String]] = Task { Seq( +trait TscModule extends NodeModule { + def npmDevDeps: Task { Seq( "typescript@5.6.3", "@types/node@22.7.8", "esbuild@0.24.0" ) } - def transitiveNpmDeps: T[Seq[String]] = Task { - Task.traverse(moduleDeps)(_.npmDeps)().flatten - } - - def transitiveNpmDevDeps: T[Seq[String]] = Task { - Task.traverse(moduleDeps)(_.npmDevDeps)().flatten - } - - def install = Task { - PathRef(nodeModulesInModuleRoot().path) - } - - def npmInstall = Task { - val deps = npmDeps() ++ transitiveNpmDeps() - if (deps.nonEmpty) { - os.call(( - "npm", - "install", - deps - )) - } - val devDeps = npmDevDeps() ++ transitiveNpmDevDeps() - if (devDeps.nonEmpty) { - os.call(( - "npm", - "install", - "--save-dev", - devDeps - )) - } - - PathRef(Task.dest) - } - - def nodeModulesInModuleRoot = Task { - val installPath = npmInstall().path - - // Copy the node_modules to the source path so that IDE can find the types. Other tasks are able to depend on this. - // Remove the old node_modules because this is a common thing to do in the JS world, also if the user did it manually, it won't repopulate if the task is cached. - os.remove.all(millSourcePath / "node_modules") - os.copy(installPath / "node_modules", millSourcePath / "node_modules", followLinks = false) - PathRef(installPath) - } - def sources = Task.Source(millSourcePath / "src") - def allSources = Task { os.walk(sources().path).filter(file => file.ext == "ts" || file.ext == "tsx" || file.ext == "d.ts").map(PathRef(_)) } + def allSources = Task { os.walk(sources().path).filter(file => file.ext == "ts" || file.ext == "tsx").map(PathRef(_)) } def tscArgs = Task { Seq.empty[String] } + def tscModuleDeps = moduleDeps.filter(_.isInstanceOf[TscModule]).map(_.asInstanceOf[TscModule]) + def compile: T[(PathRef, PathRef)] = Task { val nodeTypes = install().path / "node_modules/@types" val javascriptOut = Task.dest / "javascript" val declarationsOut = Task.dest / "declarations" val upstreamPaths = - for (((jsDir, dTsDir), mod) <- Task.traverse(moduleDeps)(_.compile)().zip(moduleDeps)) + for (((jsDir, dTsDir), mod) <- Task.traverse(tscModuleDeps)(_.compile)().zip(moduleDeps)) yield (mod.millSourcePath.subRelativeTo(Task.workspace).toString + "/*", dTsDir.path) val allPaths = upstreamPaths ++ Seq("*" -> sources().path, "*" -> install().path) @@ -94,7 +48,7 @@ trait TypeScriptModule extends Module { def mainFileName = Task { s"${millSourcePath.last}.js" } def prepareRun = Task.Anon { - val upstream = Task.traverse(moduleDeps)(_.compile)().zip(moduleDeps) + val upstream = Task.traverse(tscModuleDeps)(_.compile)().zip(moduleDeps) for (((jsDir, tTsDir), mod) <- upstream) { os.copy(jsDir.path, Task.dest / mod.millSourcePath.subRelativeTo(Task.workspace)) } diff --git a/javascriptlib/test/src/mill/javascriptlib/HelloWorldTests.scala b/javascriptlib/test/src/mill/javascriptlib/HelloWorldTests.scala index f9f60158e45..6a3b7bdafdc 100644 --- a/javascriptlib/test/src/mill/javascriptlib/HelloWorldTests.scala +++ b/javascriptlib/test/src/mill/javascriptlib/HelloWorldTests.scala @@ -9,11 +9,11 @@ import java.io.{ByteArrayOutputStream, PrintStream} object HelloWorldTests extends TestSuite { object HelloWorldJavascript extends TestBaseModule { - object foo extends TypeScriptModule { - object bar extends TypeScriptModule + object foo extends TscModule { + object bar extends TscModule } - object qux extends TypeScriptModule + object qux extends TscModule } val resourcePath = os.Path(sys.env("MILL_TEST_RESOURCE_DIR")) / "hello-world-typescript" From bd8670960c03b7c3ab926c26138b9665a41000a7 Mon Sep 17 00:00:00 2001 From: Myyk Seok <2080820+myyk@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:43:01 +0800 Subject: [PATCH 3/5] fix: compilation issue --- javascriptlib/src/mill/javascriptlib/TscModule.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascriptlib/src/mill/javascriptlib/TscModule.scala b/javascriptlib/src/mill/javascriptlib/TscModule.scala index c3e395c436e..e06709003bd 100644 --- a/javascriptlib/src/mill/javascriptlib/TscModule.scala +++ b/javascriptlib/src/mill/javascriptlib/TscModule.scala @@ -2,7 +2,7 @@ package mill.javascriptlib import mill._ trait TscModule extends NodeModule { - def npmDevDeps: Task { Seq( + def npmDevDeps = Task { Seq( "typescript@5.6.3", "@types/node@22.7.8", "esbuild@0.24.0" From 26425ba67890c70954c9deede03feb571bc51676 Mon Sep 17 00:00:00 2001 From: Myyk Seok <2080820+myyk@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:44:35 +0800 Subject: [PATCH 4/5] fix: formatting --- .../javascriptlib/basic/1-simple/build.mill | 2 +- .../src/mill/javascriptlib/TscModule.scala | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/example/javascriptlib/basic/1-simple/build.mill b/example/javascriptlib/basic/1-simple/build.mill index 06f9196915d..ea085de4f02 100644 --- a/example/javascriptlib/basic/1-simple/build.mill +++ b/example/javascriptlib/basic/1-simple/build.mill @@ -3,7 +3,7 @@ import mill._, javascriptlib._ object foo extends TypeScriptModule { object bar extends TypeScriptModule { - def npmDeps = Seq("immutable@4.3.7") + def npmDeps = Seq("immutable@4.3.7") } } diff --git a/javascriptlib/src/mill/javascriptlib/TscModule.scala b/javascriptlib/src/mill/javascriptlib/TscModule.scala index e06709003bd..a336b180000 100644 --- a/javascriptlib/src/mill/javascriptlib/TscModule.scala +++ b/javascriptlib/src/mill/javascriptlib/TscModule.scala @@ -2,18 +2,23 @@ package mill.javascriptlib import mill._ trait TscModule extends NodeModule { - def npmDevDeps = Task { Seq( - "typescript@5.6.3", - "@types/node@22.7.8", - "esbuild@0.24.0" - ) } + def npmDevDeps = Task { + Seq( + "typescript@5.6.3", + "@types/node@22.7.8", + "esbuild@0.24.0" + ) + } def sources = Task.Source(millSourcePath / "src") - def allSources = Task { os.walk(sources().path).filter(file => file.ext == "ts" || file.ext == "tsx").map(PathRef(_)) } + def allSources = Task { + os.walk(sources().path).filter(file => file.ext == "ts" || file.ext == "tsx").map(PathRef(_)) + } def tscArgs = Task { Seq.empty[String] } - def tscModuleDeps = moduleDeps.filter(_.isInstanceOf[TscModule]).map(_.asInstanceOf[TscModule]) + def tscModuleDeps: Seq[TscModule] = + moduleDeps.filter(_.isInstanceOf[TscModule]).map(_.asInstanceOf[TscModule]) def compile: T[(PathRef, PathRef)] = Task { val nodeTypes = install().path / "node_modules/@types" From 6c4bce8ccbce14aef7c506868390f3016d660282 Mon Sep 17 00:00:00 2001 From: Myyk Seok <2080820+myyk@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:50:59 +0800 Subject: [PATCH 5/5] example: fix it --- example/javascriptlib/basic/1-simple/build.mill | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/javascriptlib/basic/1-simple/build.mill b/example/javascriptlib/basic/1-simple/build.mill index ea085de4f02..d4240455634 100644 --- a/example/javascriptlib/basic/1-simple/build.mill +++ b/example/javascriptlib/basic/1-simple/build.mill @@ -1,13 +1,13 @@ package build import mill._, javascriptlib._ -object foo extends TypeScriptModule { - object bar extends TypeScriptModule { +object foo extends TscModule { + object bar extends TscModule { def npmDeps = Seq("immutable@4.3.7") } } -object qux extends TypeScriptModule { +object qux extends TscModule { def moduleDeps = Seq(foo, foo.bar) }