svgdotjs / svg.js

The lightweight library for manipulating and animating SVG
https://svgjs.dev
Other
11.04k stars 1.08k forks source link

SVG.js does not conform to AMD #1148

Closed crystalfp closed 1 year ago

crystalfp commented 4 years ago

Bug report

Loading SVG.js package (version 3.0.16) using RequireJS fails, apparently because svg.js does not conform to AMD rules.

Code to reproduce

Have Typescript (4.0.x) installed (npm i -g typescript)

Download the minimal example svg-requirejs.zip and unzip it in an empty directory, then change to this directory.

Finish installation by: npm i

Compile the example code with tsc

Run the test by loading index.html in a browser (Firefox or Chrome) and open the console to see the error.

Test cases

In client.ts there are three import statements corresponding to three test cases. Leave uncommented the one you are interested in. As written in the comments, you could have to change SVG() to SVG.SVG().

Case 1

Using the following: import SVG from "@svgdotjs/svg.js"; compilation fails:

client.ts:7:1 - error TS2349: This expression is not callable.
Type 'typeof import("@svgdotjs/svg.js")' has no call signatures.
7 SVG()

Changing the call to SVG.SVG() the compilation succeeds, but at runtime the error is: Uncaught TypeError: svg_js_1.default is undefined

Case 2

Using import * as SVG from "@svgdotjs/svg.js"; with SVG.SVG() at runtime the error is: Uncaught TypeError: SVG.SVG is not a function

Case 3

Using import {SVG} from "@svgdotjs/svg.js"; gives a runtime error:Uncaught TypeError: svg_js_1 is undefined

Explanation

A working example

If this could help, the package tippy.js has a similar main entry point as SVG.js. After adding to requirejs.config() packages section this:

{
    name: "tippy.js",
    location: "node_modules/tippy.js",
    main: "dist/tippy-bundle.umd"
}

And loading it with import tippy from "tippy.js"; it can be called as: tippy(selector, {...});

Fuzzyma commented 4 years ago

svg.js has no UMD wrapper for the global version. That is true. However, the typescript compiler should pick up the esm version anyway. To use svg.js you either load it in a script tag and can use it like v2: SVG().addTo(selector), or you use esm syntax and do:

import { SVG } from '@svgdotjs/svg.js'
SVG().addTo(selector)
crystalfp commented 4 years ago

The typescript compiler is producing AMD modules, so your import statement is compiled to a define(["require", "exports", "@svgdotjs/svg.js"], ... so I'm back to square one.

At the end I'm using a very ugly workaround. In the module using SVG I have:

import SVG from "node_modules/@svgdotjs/svg.js/dist/svg.min";
import type {Point, Svg} from "@svgdotjs/svg.js";

and in the shim section of RequireJS configuration:

"node_modules/@svgdotjs/svg.js/dist/svg.min": {
    exports: "SVG"
}

That is, I'm using SVG.js as a legacy library. It is a shame, but at least I'm able to use it together with all other libraries. Have a nice day! mario

Fuzzyma commented 4 years ago

You sound offended. I had no intention to offend you.

As far as I know, typescript support esm modules. That means you need to use the esm module, not the global version. In fact I used svg.js with typescript already and it worked just as you would expect

crystalfp commented 4 years ago

No, I'm not offended. Sorry if I give this impression.

Is that I'm trying to recover my application from a monumental misunderstanding, that is, I was adding AMD modules by hand instead of letting typescript compile them. And was hoping in a smooth transition to the correct module usage. But seems it is really hard with some libraries to make Typescript and RequireJS collaborate. With other libraries instead was smooth as silk.

The workaround I mentioned is more or less the same as your suggestion even if I have to add // @ts-uncheck before because I still are not able to satisfy the typescript requirement of pointing to the right .d.ts file for the library.

Anyway, thanks. I'm going to fight with the next library...

Fuzzyma commented 4 years ago

I can really relate to that :D.

Hm, if typescript does not pick up the right file, you can try to point to dist/svg.esm.js or src/svg.js. The second one is untranspiled but typescript should handle that. Both export all the stuff according to the d.ts file

nuwannnz commented 3 years ago

I'm also having this issue in a TypeScript project. I import the SVG like this import {SVG} from "@svgdotjs/svg.js"

Then when compiling I get this error, SyntaxError: 'import' and 'export' may appear only with 'sourceType: module' (11122:0) while parsing /~/node_modules/@svgdotjs/svg.js/dist/svg.esm.js

I couldn't get the workaround of @crystalfp to work. Is there any other way to solve this?

Fuzzyma commented 3 years ago

I dont know. If possible tell typescript to load the file with sourceType:module because that is what seems to be required from typescript

tmirun commented 3 years ago

+1 this problem

crystalfp commented 3 years ago

@tmirun My solution is not pretty, but at least solved the problem. 1) In the source I import SVG.js this way:

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
import SVG from "../node_modules/@svgdotjs/svg.js/dist/svg.min";

2) Import the SVG types this way:

import type {Point, Svg} from "@svgdotjs/svg.js";

3) In the shim section of requirejs.config() I added:

    shim: {
        "../node_modules/@svgdotjs/svg.js/dist/svg.min": {
            exports: "SVG"
        },

Then I can call and use SVG.js Hope it helps mario

P.S.: In another project I used svg native, it is not much more complex.

tmirun commented 3 years ago

hey @crystalfp thank your solution, the 1rt one resolve my problem

thanks