LinbuduLab / esbuild-plugins

ESBuild plugins by @LinbuduLab
MIT License
113 stars 18 forks source link

Copy plugin "is not a function" #66

Closed RobinNiemann closed 2 years ago

RobinNiemann commented 2 years ago

I'm working on an esbuild configuration and want to copy a file (manifest.json) to a specific folder when I build my project for testing purposes. I included the copy plugin into my esbuild-config.mjs

import esbuild from "esbuild";
import process from "process";
import builtins from 'builtin-modules'
import copy from 'esbuild-plugin-copy';

const prod = (process.argv[2] === 'production');

const baseConfig = {
    // ...
};

const testVaultPluginFolder = 'test-vault/.obsidian/plugins/obsidian-sample-plugin/';
const devConfig = {
    ...baseConfig,
    outfile: testVaultPluginFolder + 'main.js',
    plugins: [
        copy({  // <- This line causes the problem
            assets: [
                { from: ['manifest.json'], to: [testVaultPluginFolder] }
            ]
        })
    ]
};

const prodConfig = {
    ...baseConfig,
    outfile: 'main.js',
};

if (prod){
    esbuild.build(prodConfig).catch(() => process.exit(1));
} else {
    esbuild.build(devConfig).catch(() => process.exit(1));
}

I can even Ctrl + Click into the copy function in VS Code.

Then when I run it it tells me that copy is not a function:

$ npm run dev

> obsidian-sample-plugin@1.1.0 dev C:\Workspaces\RNSS-Sample\obsidian-sample-plugin
> node esbuild.config.mjs

file:///C:/Workspaces/.../obsidian-sample-plugin/esbuild.config.mjs:59
                copy({
                ^

TypeError: copy is not a function
    at file:///C:/Workspaces/.../obsidian-sample-plugin/esbuild.config.mjs:59:3
    at ModuleJob.run (internal/modules/esm/module_job.js:170:25)
    at async Loader.import (internal/modules/esm/loader.js:178:24)
    at async Object.loadESM (internal/process/esm_loader.js:68:5)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! obsidian-sample-plugin@1.1.0 dev: `node esbuild.config.mjs`
npm ERR! Exit status 1

Edit: Further research

I found this question where the copy (to clipboard) function produced the same error: https://stackoverflow.com/questions/62212958/devtools-console-copy-is-not-a-function-while-on-youtube

The problem was, that the DOM contained another element named copy that was not a function. Even though I'm not in a Browser here, I tried renaming the import.

import copyIsADamnFunction from 'esbuild-plugin-copy';

plugins: [
    copyIsADamnFunction({
        assets: [
            { from: ['manifest.json'], to: [testVaultPluginFolder] }
        ]
    })
]

Same result:

TypeError: copyIsADamnFunction is not a function
    at file:///C:/Workspaces/.../obsidian-sample-plugin/esbuild.config.mjs:59:3

When I remove the import I get

ReferenceError: copy is not defined
    at file:///C:/Workspaces/.../obsidian-sample-plugin/esbuild.config.mjs:58:11

So the import imports something. And VS Code tells me on mouse over that copy is a function with one optional parameter returning a esbuild.Plugin:

(alias) copy(options?: Partial<Options>): esbuild.Plugin
import copy

If I write copy. VS Code even offers me code completion like apply, arguments, bind and call which all belong to Function.prototype. How can copy not be a function?

uncle-ara commented 2 years ago

Same problem. Looking for an answer

linbudu599 commented 2 years ago

Seems like problem lead by incorrect compiler options, this will be fixed in next version which will released soon this weekend.

uncle-ara commented 2 years ago

Seems like problem lead by incorrect compiler options, this will be fixed in next version which will released soon this weekend.

Thank you

linbudu599 commented 2 years ago

@uncle-ara @RobinNiemann The latest release 0.5.1 should have fixed the problem as I'm now using the pure tsc as compiler.

Both named import and default import are supported.

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.copy = void 0;
const esbuild_plugin_copy_1 = __importDefault(require("./lib/esbuild-plugin-copy"));
exports.copy = esbuild_plugin_copy_1.default;
exports.default = esbuild_plugin_copy_1.default;
BrunoViera commented 2 years ago

Hello, sorry but I just try it and it's not working with the example that is in the documentation => import copy from 'esbuild-plugin-copy';, to make it work you need to do this way => import { copy } from 'esbuild-plugin-copy';

Thanks!

linbudu599 commented 2 years ago

Hello, sorry but I just try it and it's not working with the example that is in the documentation => import copy from 'esbuild-plugin-copy';, to make it work you need to do this way => import { copy } from 'esbuild-plugin-copy';

Thanks!

This is weird, what error occurs when you're trying to use the default import?

uncle-ara commented 2 years ago

@linbudu599 when the release 0.5.1?

linbudu599 commented 2 years ago

@linbudu599 when the release 0.5.1?

Use latest version 1.0.1, as some features were added(#67 #68 #57 )

uncle-ara commented 2 years ago

@linbudu599 when the release 0.5.1?

Use latest version 1.0.1, as some features were added(#67 #68 #57 )

image The root folder depends on the outfile. Is there any way to change the root folder just for copying files?

linbudu599 commented 2 years ago

@linbudu599 when the release 0.5.1?

Use latest version 1.0.1, as some features were added(#67 #68 #57 )

image The root folder depends on the outfile. Is there any way to change the root folder just for copying files?

Did you mean use process.cwd() to resolve to path?

linbudu599 commented 2 years ago

I think maybe we can add new option resolveFrom which accepts cwd|out or any other path string, and this option will be used to resolve final asset.to path, do you think this could help?

uncle-ara commented 2 years ago

I think maybe we can add new option resolveFrom which accepts cwd|out or any other path string, and this option will be used to resolve final asset.to path, do you think this could help?

Yes, that would solve the problem

linbudu599 commented 2 years ago

This is supported in version 1.1.0 now, you can refer to https://nx-plugins.netlify.app/derived/esbuild.html#copy to check details. Feel free to report if you got any troubles!

uncle-ara commented 2 years ago

This is supported in version 1.1.0 now, you can refer to https://nx-plugins.netlify.app/derived/esbuild.html#copy to check details. Feel free to report if you got any troubles!

thank you

uncle-ara commented 2 years ago

This is supported in version 1.1.0 now, you can refer to https://nx-plugins.netlify.app/derived/esbuild.html#copy to check details. Feel free to report if you got any troubles!

Something is not working, please believe me resolveFrom image

linbudu599 commented 2 years ago

I think the problem may come from the fact that you are using *.* pattern, the default globby option has expandDirectories disabled, so if this directory is nested, you need to use ** pattern.

uncle-ara commented 2 years ago

I think the problem may come from the fact that you are using *.* pattern, the default globby option has expandDirectories disabled, so if this directory is nested, you need to use ** pattern.

still doesn't work :( image

linbudu599 commented 2 years ago

Can you provide some more precise information, including what you mean by not working, and your directory structure? You can also upgrade to 1.2.1 add use the new dryRun option to see what's happenning.

I'm always here to help.

RobinNiemann commented 2 years ago

I just came back from my vacation, happy to see all the movement here :-)

I just retested my project with version 1.2.1.

import copy from 'esbuild-plugin-copy';

This import still doesn't work ("copy is not a function").

However, the solution provided by @BrunoViera works for me:

import { copy } from "esbuild-plugin-copy";

So I can continue with my project, which is great :-) Unfortunately the import without the curly brackets is the auto import from Visial Studio Code so it's kinda easy to fall into that trap. Maybe the import without the curly brackets can be made working too?

And since there is still discussion about the resolveFrom I'll leave this issue open. I also didn't expect the to folder to be resolved from the outfile path. But thats no big concert to me.

linbudu599 commented 2 years ago

First of all I hope you had a great holiday. About the weird import statement, I'm now using this syntax to support both named import(without the curly brackets) and default import(without the curly brackets):

import { copy as _copy } from './lib/esbuild-plugin-copy';

export const copy = _copy;

export default copy;

export { Options, AssetPair } from './lib/esbuild-plugin-copy';

which works correctly for me to use both default import statement and named import statement. Could you please provide a minimal repro for me to check what is the problem? By the way I also updated the docs to use the named import as which seems to work better.

About the resolveFrom, it's indeed a late capability for the copy plugin.

RobinNiemann commented 2 years ago

To reproduce you can check out this repository.

Remove the brackets from

import { copy } from "esbuild-plugin-copy";

and then run

npm run dev

This should result in

    TypeError: copy is not a function
        at file:///C:/Workspaces/RNSS-Sample/obsidian-sample-plugin/esbuild.config.mjs:59:3
        at ModuleJob.run (internal/modules/esm/module_job.js:170:25)
        at async Loader.import (internal/modules/esm/loader.js:178:24)
        at async Object.loadESM (internal/process/esm_loader.js:68:5)
    npm ERR! code ELIFECYCLE
    npm ERR! errno 1
    npm ERR! obsidian-sample-plugin@1.1.0 dev: `node esbuild.config.mjs`
    npm ERR! Exit status 1
linbudu599 commented 2 years ago

@BrunoViera I think I got the reason, you're using .mjs to enable ESM in nodejs, whereas this plugin was built as CJS so it exports funciton like this: exports.copy and exports.default, so when you're using default import (without brackets) the import value would be: { copy: Function, default: Function}.

I have now updated the example in the documentation to a named import, which is the best way to be compatible for both CJS and ESM.