-
Notifications
You must be signed in to change notification settings - Fork 280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support TypeScript usage without compile step #297
Comments
If the user wants to use TS, would it be safe to assume they have already added tsc to the project? If so, could logic be something like... This adds support for TS, without adding more dependencies that are likely not needed. Thoughts? |
Would that work if the Also, projects might have TS loaders hooked up, like when using deno or ts-node instead of plain node, right? 296 looks like he installed the ts-node loader, but wasn't actually registering it? I haven't tried it, but https://github.com/TypeStrong/ts-node#node-flags looks like it. |
@amwmedia this would be nice, but unfortunately @Pike is quite right - imports would fail. What's worse, one of the reasons that What's more - we wouldn't be able to get the return value from Instead, what I might suggest is that we use a Node loader from https://github.com/unicorn-utterances/unicorn-utterances/blob/nextjs/package.json#L6 https://www.npmjs.com/package/esbuild-node-loader This will handle .ts, .tsx, and other files OOTB for us, without having to change much. ESBuild is supported by huge projects like Vite. |
I just looked into the viability to do this for real and there's a few minor problems we'll need to sort out first:
#2 is occurring because of our Luckily, this is already fixed for us in To see the branch I started to POC this idea (just starting with e2e tests of running |
I was able to get esbuild-node-loader working really well with the only issue I ran into was the missing comma which is now fixed (plopjs/node-plop#215). Here's an example repo I made that shows it working: Happy to help any other way I can! |
ts-node's ESM support should cover plop's use-cases, including skipping typechecking and using a native transpiler for speed. If users have configured it for other parts of their project, we'll pick up that config automatically, since it's the same. Let me know if you have questions. |
Any updates on this? |
|
so i've kinda worked around this with: "
with a {
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "node"
},
// Most ts-node options can be specified here using their programmatic names.
"ts-node": {
"swc": true,
"esm": true,
"pretty": true,
// It is faster to skip typechecking.
// Remove if you want ts-node to do typechecking.
"transpileOnly": true,
"files": true,
"compilerOptions": {
"module": "CommonJS"
// compilerOptions specified here will override those declared below,
// but *only* in ts-node. Useful if you want ts-node and tsc to use
// different options with a single tsconfig.json.
}
}
}
then in my ...
#
# Generator
#
alias gen := generate
alias g := generate
generate *ARGS:
yarn ts-node \
./node_modules/plop/bin/plop.js {{ARGS}}
...
and then the import type { NodePlopAPI } from 'plop';
module.exports = function Plopfile(plop: NodePlopAPI) {
plop.setGenerator('test', {
prompts: [
{
type: 'confirm',
name: 'wantTacos',
message: 'Do you want tacos?',
},
],
actions: [],
});
};
resulting in: Screencast.from.2023-03-26.22-25-22.webm |
I played around trying to make this work, and found success with With concurrently package in run.ts #!/usr/bin/env node
/* eslint-disable import/no-extraneous-dependencies */
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import minimist from 'minimist';
import { Plop, run } from 'plop';
const args = process.argv.slice(2);
const argv = minimist(args);
Plop.prepare(
{
cwd: argv?.cwd as string,
configPath: join(
dirname(fileURLToPath(import.meta.url)),
'plopfile.js',
),
},
// eslint-disable-next-line @typescript-eslint/no-misused-promises
(env) => Plop.execute(env, run),
); Maybe someone here with more expertise can explain why when running |
I can say with a high degree of certainty, that the following setup works with the following conditions: Package Versions
|
This comment was marked as outdated.
This comment was marked as outdated.
@moltar I appreciate you suggesting this, but I think the fix might be even "simpler" (conceptually) than that. See, we're using Gulp's Liftoff library to detect configuration files: https://github.com/plopjs/plop/blob/main/packages/plop/src/plop.js#L5C22-L5C29 Which allows you to enable TS support through this mechanism: |
@crutchcorn Well, that is amazing! 😁 Going to mark my comment as hidden to avoid confusion. |
You'll all be happy to know that I've just implemented this functionality in Plop v4: It turns out that it was even easier than anticipated from the easy method outlined in my last comment. |
This should be solved in Plop 4.0! |
Thanks for the update! Just tried the example config from the tests and sadly could not get this to work: https://github.com/plopjs/plop/tree/main/packages/plop/tests/examples/typescript Receiving the following error: [PLOP] Something went wrong with reading your plop file TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for C:\dev\plopfile.ts
at new NodeError (node:internal/errors:399:5)
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:79:11)
at defaultGetFormat (node:internal/modules/esm/get_format:121:38)
at defaultLoad (node:internal/modules/esm/load:81:20)
at nextLoad (node:internal/modules/esm/loader:163:28)
at ESMLoader.load (node:internal/modules/esm/loader:605:26)
at ESMLoader.moduleProvider (node:internal/modules/esm/loader:457:22)
at new ModuleJob (node:internal/modules/esm/module_job:64:26)
at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:480:17)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:434:34) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
} |
Hi @crutchcorn |
It appears |
Hi @noahgregory-basis
|
The package name fails to resolve. It does not appear to be public in the NPM registry (or Yarn registry for that matter). |
@crutchcorn @amwmedia I will be very grateful for your response 🙂 |
@cspotcode |
Also worked for me! Thanks @cspotcode! |
hey hey @crutchcorn, thx for the great tool and the effort to support ts! it is running fine for me! i have a little caveat. i am in a nx project and would like to use functionalities from our lib projects, like validators, i already wrote for other parts of our project. my plopgenerator looks like this
this fails with
without the path alias import it is running fine |
Not having native .ts support is breaking a few projects I work on that have linting / rules to ensure there's no non-ts js files in repos. It would be good to have this added. |
If using Deno or Bun, I'm wondering if less lifting is required, but for now I'll take TS support in whatever flavor it comes in. |
FWIW, beyond Yarn 2 support (which might be another major issue that I'd need to test first), what @divramod raised is a legit concern that I didn't consider when building; I need some way to allow users to bypass loading in TSX automatically. Overall, I'm not super happy with how my Apologies y'all. I'll try to come back to this in 2024 when some other priorities I have on the table shake out more. In the meantime, if someone wanted to contribute, please feel free to take my PR and add in a |
I don't know if it is helpful. I solved the typescript configuration issue by transpiling it to JavaScript. Furthermore, I used SWC as transpiler because it was just faster as yarn add -D rimraf @swc/cli @swc/core File: {
...
"scripts": {
...
"plop": "npx rimraf ./plopfile.js && npx swc ./plopfile.ts --out-dir . && plop"
},
...
} File: {
"$schema": "https://json.schemastore.org/swcrc",
"minify": false,
"module": {
"type": "commonjs",
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
},
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "esnext",
"loose": false,
"externalHelpers": false,
"keepClassNames": false
},
"isModule": true
} File: {
"compilerOptions": {
"target": "ES6",
"allowJs": true,
"module": "commonjs",
"skipLibCheck": true,
"esModuleInterop": true,
"noImplicitAny": true,
"sourceMap": true,
"baseUrl": ".",
"outDir": "dist",
"moduleResolution": "node",
"resolveJsonModule": true,
"paths": {
"*": [
"node_modules/*"
]
},
"jsx": "react"
},
"include": [
"src/**/*"
]
} File: import {NodePlopAPI} from 'plop';
module.exports = function (plop: NodePlopAPI) {
// controller generator
plop.setGenerator('controller', {
description: 'application controller logic',
prompts: [{
type: 'input',
name: 'name',
message: 'controller name please'
}],
actions: [{
type: 'add',
path: 'src/{{name}}.js',
templateFile: 'plop-templates/controller.hbs'
}]
});
}; That works for me: |
I improved my solution with the help of md5sum: Previously File: package.json {
...
"scripts": {
...
"plop": "npx rimraf ./plopfile.js && npx swc ./plopfile.ts --out-dir . && plop"
},
...
} --> changes to a ...
"scripts": {
...
"plop": "md5sum --check --status plopfile.md5 || yarn run plop::transpile && plop",
"plop::transpile": "echo 'Deleting plopfile.js and regenerate it+md5' && npx rimraf ./plopfile.js && npx swc ./plopfile.ts --out-dir . && md5sum plopfile.ts > plopfile.md5",
}
... It's fast(er), works great. |
A twist on @tobiashochguertel's solution, using "plop": "md5sum --check --status plopfile.md5 || pnpm plop:transpile && plop",
"plop:transpile": "echo 'Deleting plopfile and regenerate it+md5' && rimraf ./plopfile.js && tsup ./plopfile.ts --format esm --out-dir . && md5sum plopfile.ts > plopfile.md5", |
@benallfree just as a heads up, I generally discourage folks from using tsup these days because their .d.ts generation has been buggy for us on TanStack projects (as a maintainer of TanStack, not consumer) |
@crutchcorn Thank you, yes #423 is a better approach: "scripts": {
"plop": "cross-env NODE_OPTIONS='--import tsx' plop --plopfile=plopfile.ts",
} |
@crutchcorn what do you recommend instead of tsup? |
|
@Nicholaiii In this case tsx is preferred instead of actually generating a bundle. See this discussion. "scripts": {
"plop": "cross-env NODE_OPTIONS='--import tsx' plop --plopfile=plopfile.ts",
} |
I saw jiti in the comments, just a hint how to add support for // try-require.ts
import jiti from "jiti";
// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/explicit-module-boundary-types
const tryRequire = (id: string, rootDirectory: string, errorReturn: any): any => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const _require = jiti(rootDirectory, { esmResolve: true, interopDefault: true });
try {
return _require(id);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error.code !== "MODULE_NOT_FOUND") {
console.error(new Error(`Error trying import ${id} from ${rootDirectory}`, {
cause: error,
}));
}
return errorReturn;
}
};
export default tryRequire; config loding: const config = tryRequire("./plopfile", cwd, undefined); If you would accept a PR like this, i would be happy to add it :) |
Regrettably, at this time we're moving forward with a "We won't support this feature" in Plop without additional configuration today. Instead, we're going forward with:
This is done via this PR: #428 and is now merged. |
Today, we support:
.js
plopfile.mjs
plopfile.js
plopfile.cjs
plopfileIt would be nice if it also handled:
.ts
plopfileWithout needing to add a compilation step. This would likely be done by:
tsc
to output the single JS file to a temporary directorytsconfig.json
fileThe text was updated successfully, but these errors were encountered: