brainfoolong/form-data-json

Document how to import this module

Closed this issue · 18 comments

I download form-data-json.js into my package and try to import this module:

const FormDataJson = require('../common/form-data-json.js');

export function ajaxForm(form) {
  const formData  = FormDataJson.toJson( form );

But get error: FormDataJson is not defined

How to import this module?

Hm, do you mean this feature? https://www.digitalocean.com/community/tutorials/js-modules-es6

This have import/export but not require as you have suggested.

This lib does support IE11, i doubt that it is possible to support ES6 modules because of syntax errors in older browsers.

I download and copy form-data-json.js into my project.

I use webpack and at my main.js

I do this:
https://github.com/brainfoolong/form-data-json/tree/v2-dev#installation
image

const xx =  require( "./common/form-data-json.js" );

window.addEventListener("DOMContentLoaded", (event) => {
  debugger;
  xx.toJson();
});
const webpack = require("webpack");
const path = require("path");
const glob = require("glob");
const config = require("./gulp/config");
module.exports = {
  entry: toObject(glob.sync(config.scripts.srcScripts)),
  output: {
    filename: "[name].js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: `babel-loader`,
      },
    ],
  },
  mode:    config.mode.isDevMode ? "development" : "production",
  devtool: config.mode.isDevMode ? "eval-source-map" : "none",
};
function toObject(paths) {
  const entry = {};
  paths.forEach(function (p) {
    const name = path.basename(p, ".js");
    entry[name] = p;
  });
  return entry;
}

and get error:
image

As far as I can understand, this file should be loaded as CommonJS and not ES module.
But it is loaded as ES.

So I need not only require as described here: https://github.com/brainfoolong/form-data-json/tree/v2-dev#installation

The problem here probably because I try to load CommonJS module from my ES modules

Finally I resolve this.

We should be aware of: https://webpack.js.org/api/module-methods/

Actually webpack would enforce the recommendation for .mjs files, .cjs files or .js files when their nearest parent package.json file contains a "type" field with a value of either "module" or "commonjs". Please pay attention to these enforcements before you read on:

  *  .mjs or .js with "type": "module" in package.json
    No CommonJS allowed, for example, you can't use require, module.exports or exports
    File extensions are required when importing, e.g, you should use import './src/App.mjs' 
    instead of import './src/App' (you can disable this enforcement with Rule.resolve.fullySpecified)

So because I copy form-data-json.js into my project as .js then webpack load it as ES module. Which cause error above.
So I must copy file as .cjs and EXPLICITLY import it with EXENTION!!! eg:

import FormDataJson from "./form-data-json.cjs";

Ok. I think there is a confusion of the NPM part in the readme. This is meant only for NodeJS, i'll update the description.

When you use webpack, you should pack the original js file directly in your bundle that is integrated on your page. This is how most JS libraries work, i guess? Loading it afterwards with an import is not supported out of the box.

When you use webpack, you should pack the original js file directly in your bundle that is integrated on your page.

Yes, I also speak about bundle
It is not packed because import FormDataJson from "./form-data-json.js"; is loaded as ES module and fail.
But if I rename it to .cjs it works well. This is because your module is CommonJS module ;-)

So I suppose to see a section at documentation for webpack, eg: If you use webpack you must rename module as .cjs ... bla-bla-bla

Hm.... How do you add other libraries in webpack? As far as i understand webpack from the docs correctly, you can integrate any JS files directly in the bundle (via webpack config). Here you can include any JS file you want.

This is how it is supposed to work with any 3rd party library which is globally available (like jquery, vuejs, svelte, etc...), isn't it?

Only when you use a library that supports module behaviour AND if you want specifically the import feature (which i don't suggest for this library, because probably of multiple imports of the same file), without packing it directly in the webpack config, than you have to rename it to .cjs.

I see, a very special case. I'll add a readme entry about using this lib as an import module.

I reread my messages and probably they could look a bit aggressive because of bold and capital letters. Yes, this emotions but emotion like holiday, celibrate, hoooray, hooray etc ;-)

Here is my layout:
image

When I build my project then webpack will put all src/js/**/*js files into one file dist/main.js

Scripts which make use of form-data-json do import. For example file js/forms/ajaxForm.js first line is:

 import FormDataJson from "../common/form-data-json.cjs";

The important thing here is only extension. It must be .cjs. All other stuff will be handled automatically by webpack.

I don't understand them as offense, no problem.

Isn't FormDataJson available without any import in your case, as it is already in the main.js? (F12 -> console.log(FormDataJson)
But probably you do try to call it before they included in the code?

PS: This library is automatically in global namespace without import, as soon as you have loaded/packed it in a file that is loaded via <script> tag.

nope, it is not available global and it is OK. FormDataJson is scoped to module which make import. When I want to make it globally available to all my modules, then I must expose it or do webpack.config.js similar jQuery:
image

And when I include my resulting bundle js/main.js from my html, FormDataJson is still not available globally. I suppose this is feature of webpack.

When I include module at html file as <script src="js/form-data-json.js"></script> then Yes, FormDataJson is available globally

Here is how it work if I load form-data-json.js directly (last case above)
image

image

Ahhh, ok now i understand. Webpack capsulate it and only the config Webpack.ProvidePlugin make it into the global namespace.

If this work (if you can confirm), this should be the recommended way, without renaming files and without import.

For sure, i'll add both ways into Readme, albeit a webpack user probably already know the tricks to expose 3rd party libs.

probably webpack users know that =) Unfortunately I do not know Webpack.ProvidePlugin way

I am more backend developer and work with library since insofar

Can you try adding the following with your plugins config?

new webpack.ProvidePlugin({
  FormDataJson: 'FormDataJson'
})

image

Probably your idea will work if your module will be installed via NPM, then ProvidePlugin could find it by name

new webpack.ProvidePlugin({
  FormDataJson: 'form-data-json-convert'
})

Hm. Ok thx for trying. I don't have other ideas :)

This works

module.exports = {
  entry: toObject(glob.sync(config.scripts.srcScripts)),
  output: {
    filename: "[name].js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: `babel-loader`,
      },
      {
        test: require.resolve("jquery"),
        loader: 'expose-loader',
        options: { exposes: [ '$', 'jQuery' ] },
      },
      {
        test: path.resolve(path.join(__dirname, 'src/js/common/form-data-json.cjs')),
        loader: 'expose-loader',
        options: { exposes: 'FormDataJson' },
      },
    ],
  },

Ok. I decided to not add a readme in the library description for this, as webpack has it's own rules and documentation on their side. This library does work the same as every other library, so a webpack user should search for their specific solution in the docs from the bundler itself.

Otherwise there is no end. We for example use another bundler internally, which has it's own rules how to integrate this library. But that's not part of the library documention.

Thanks for your input anyway.

NPM is an exception in the readme, as i officialy have an NPM module entry in the offical NPM registry.