How to use "import" in a polymer element - javascript

I'm trying something like the following:
<dom-module id="my-app">
<template> <div>{{data.name}}</div> </template>
<script>
import data from 'data';
class MyApp {
constructor() { ... }
beforeRegister() {
this.is = 'my-app';
this.properties = {
data: {
type: Object,
value: function () {
return data;
},
}
};
}
ready() {}
...
}
</script>
</dom-module>
So, I have here ES6 too, so I constructed the following gulp task:
gulp.task('js', function () {
return gulp.src(['app/**/*.{js,html}'])
.pipe($.sourcemaps.init())
.pipe($.if('*.html', $.crisper())) // Extract JS from .html files
.pipe($.if('*.js', $.babel()))
.pipe($.sourcemaps.write('.'))
.pipe(gulp.dest('.tmp/'))
.pipe(gulp.dest('dist/'));
});
});
This works as long as I don't have the import data from 'data' line. Now I get the following error in the browser:
require is not defined
So, I decided to add browserify to the process just below babel
.pipe($.if('*.js', $.browserify({ debug: true, extensions: ['.js', '.html'] })))
Now I get a transpile error
ParseError: 'import' and 'export' may appear only with 'sourceType: module'
I think I'm close, but I'm now stuck here unfortunately. To reproduce this I've created a github repo here
You can check it out and do:
$> cd polymer-es6
$> npm install
$> bower install
$> gulp js
A bonus question: Why is the constructor not called ? It seems that ready is called instead.

Related

How to integrate es6 with gulp-develop-server

I am trying to transfer an old node-express project over to be able to use es6. I have seen many posts about using gulp with es6. Most of them discuss using a syntax like this:
const gulp = require("gulp");
const babel = require("gulp-babel");
gulp.src('./index.js')
.pipe(
babel({
presets: [
["#babel/env", { modules: false }],
],
})
)
However my existing project's gulpfile does't use gulp.src at all. Instead, it uses gulp-develop-server. The gulpfile looks like this:
const gulp = require("gulp");
const devServer = require("gulp-develop-server");
const spawn = require("child_process").spawn;
const fs = require("fs");
const basedir = ".";
function serverRestart(done) {
// perform some cleanup code here
devServer.restart();
done();
}
function serverStart() {
devServer.listen({
path: basedir + "/index.js",
});
}
function serverWatch() {
serverStart();
gulp.watch(
[
basedir + "/paths/**/*",
// more directories to watch
],
serverRestart
);
}
function reload(done) {
serverWatch();
done();
}
function defaultTask() {
let p;
gulp.watch(["gulpfile.js"], killProcess);
spawnChild();
function killProcess(e) {
if (p && !p.killed) {
devServer.kill();
p.kill("SIGINT");
spawnChild();
}
}
function spawnChild() {
p = spawn("gulp", ["reload"], { stdio: "inherit" });
}
}
process.stdin.resume();
process.on("exit", handleExit.bind(null, { cleanup: true }));
process.on("SIGINT", handleExit.bind(null, { exit: true }));
process.on("uncaughtException", handleExit.bind(null, { exit: true }));
function handleExit(options, err) {
// perform some cleanup code here
if (options.cleanup) {
devServer.kill();
}
if (err) {
console.log(err.stack);
}
if (options.exit) {
process.exit();
}
}
gulp.task("serverRestart", serverRestart);
gulp.task("serverStart", serverStart);
gulp.task("serverWatch", serverWatch);
gulp.task("reload", reload);
gulp.task("default", defaultTask);
The existing flow is important because it executes needed code for setup and cleanup every time I hit save, which runs serverRestart. I've been trying a few different methods based on the other questions which recommended using gulp.src().pipe(), but I havne't had much luck integrating it with the existing pattern which uses gulp-develop-server. I am trying to not have to rewrite the whole gulpfile. Is there a simple way to integrate babel with my existing gulpfile such that I can use es6 in my source code?
There's an example with CoffeeScript in the gulp-develop-server documentation.
Using that as a model, try this:
function serverStart() {
devServer.listen({
path: "./dist/index.js",
});
}
function serverWatch() {
serverStart();
gulp.watch(
[
basedir + "/paths/**/*",
],
serverRestart
);
}
function serverRestart() {
gulp.src('./index.js')
.pipe(
babel({
presets: [
["#babel/env", { modules: false }],
],
})
)
.pipe( gulp.dest( './dist' ) )
.pipe( devServer() );
}
Other suggestions
That being said, your existing Gulp file doesn't actually really use Gulp. That is, everything is defined as a function and it doesn't leverage any of Gulp's useful features, like managing task dependencies. This is because (pre-es6), this was a very simple project. The Gulp tasks in that file are an over-elaborate way to watch files and run a server. The same could be done (with less code) using nodemon.
With the introduction of React and more complicated build processes, Gulp seems to have fallen out of favor with the community (and in my personal experience, Gulp was a time sinkhole anyhow).
If the main change you want to make is to use import, you can simply use a more recent Node version. You'll surely run into the error SyntaxError: Cannot use import statement outside a module. Simply rename the file to .mjs and it will work. This provides a way to incrementally migrate files to import syntax. Other features should automatically work (and are all backwards-compatible, anyhow). Once your project is mostly, or all, compliant, you can add "type": "module" to your package.json file, then rename all of your require-style js files to .cjs, and rename all of your .mjs files to .js, or leave them as .mjs. Read more about the rules of mixing CommonJS and Module imports in the Node.js blog post (note that some things may have changed since that article was written).

Importing hash-set module not working for typescript/systemjs?

I'm working on an aurelia project, using typescript to create the javascript. Now I tried to add another custom library, 'hash-set' (using jspm install npm:hash-set --save). However I can't seem to actually use this package (using systemjs as loader).
My document structure is like:
\
dist\
src\
app.html
app.js
main.js
jsp_packages\
npm\
hash-set#1.0.1\
node_modules\
index.html
config.js
package.json
tsconfig.json
The important files (I think, please state in the comments if I miss something):
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body aurelia-app="src/main">
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
SystemJS.import('aurelia-bootstrapper');
</script>
</body>
</html>
app.ts
This is compiled to app.js as prebuilt step. Using es2015 as target configuration.
import {hashSet} from 'hash-set';
export class App {
public myText: string;
hashFn(value) {
return value.toString();
}
constructor() {
alert("oh");
const h = hashSet;
const StringSet = hashSet(this.hashFn);
alert('oh2');
}
}
config.js
System.config({
defaultJSExtensions: true,
transpiler: false,
paths: {
"*": "dist/*",
"github:*": "jspm_packages/github/*",
"npm:*": "jspm_packages/npm/*"
},
meta: {
"bootstrap": {
"deps": [
"jquery"
]
}
},
map: { /*lots of aurelia and other library stuff*/
"hash-set": "npm:hash-set#1.0.1"
}
}
});
And it's also listed in package.json # {"jspm":{"dependencies":"hash-set": "npm:hash-set#^1.0.1"}}}
Now when I try to run above code (typescript compiles to app.js as prebuilt step), the app.js/app.ts loads, as expected. (frankly removing the hash-set specific code makes everything work as expected).
However during construction "oh" is shown, but "oh2" is never. Debugging over the code shows that "hashSet" is "undefined". Which leads me to believe that that systemjs isn't including the hash-set correctly?
Am I missing something?
EDIT: digging into the generated js (app.js) file I notice something weird:
define(["require", "exports", "hash-set"], function (require, exports, hash_set_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class App {
hashFn(value) {
return value.toString();
}
constructor() {
alert('oh');
const h = hash_set_1.hashSet;
const StringSet = hash_set_1.hashSet(this.hashFn);
alert('oh2');
}
}
exports.App = App;
});
//# sourceMappingURL=app.js.map
While debugging, hash_set_1 is actually of the type I expect hash_set_1.hashSet to be. Actually manually editing the javascript to not use hash_set_1.hashSet but rather just hash_set_1 works.
Trying
import hashSet from 'hash-set'; (notice lack of {}) changes the generated javascript offending line to const StringSet = hash_set_1.default(this.hashFn); which is still not correct (default isn't defined either).
If you look at the code, you'll see it is exported as:
module.exports = function hashSet(hashFn) {
Doing import { hashSet } from 'hash-set'; cannot work because the export would have to be module.exports.hashSet = ....
It should work if you do:
import hashSet = require("hash-set");

RequireJS pass config to module always returns undefined

I've tried the solution in other SO post, e.g. this, but it didn't seem to work. I always got undefined as a result.
My configuration in my HTML.
<script type="text/javascript">
var require = {
config: {
'config': { --> if I change this to app, I could read the value in app.js
userId: 1
}
}
};
</script>
<script src="lib/require.min.js" data-main="app"></script>
My config module:
define(['module'], function (module)
{
return {
userId: module.config().userId,
userName: "Test"
}
});
I use the config module in my app.
require(['modules/config', 'modules/A'], function (config, a)
{
var userId = config.userId; --> undefined
var userName = config.userName; --> Test
});
Any idea? Did I miss something? I use JQuery 1.12.3 and RequireJS 2.2.0.
Solution
Edit the config you pass to RequireJS to:
var require = {
config: {
'modules/config': {
userId: 1
}
}
};
Explanation
Your modules ids are not matching. When you require your module, you use require(['modules/config', .... So the module name is modules/config. However, in your RequireJS configuration you provide a configuration for the module named config. So when you use module.config() inside modules/config, RequireJS searches for a module named modules/config in the configuration and does not find it. So you get undefined. Performing the fix I mentioned earlier will allow RequireJS to find the configuration.
Alternatively you could use paths to have the module be loaded under the name config:
var require = {
paths: {
config: 'modules/config'
},
config: {
config: {
userId: 1
}
}
};
And use require(['config', ....
I followed the below technique to get it working. The files included in HTML. They can defined in config paths.
Directory structure:
-index.html
-modules
- myModule.js
-app.js
HTML (index.html)
<script type="text/javascript">
// require would refer to the require.js global. So i am using requireVar
var requireVar = {
config: {
userId: 1
}
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.min.js"></script>
<script src="modules/myModule.js"></script>
<script src="app.js"></script>
file myModule.js
define("modules/myModule",function ()
{
return function(){
return requireVar.config.userId;
}
});
and finally my app.js
require(['modules/myModule'], function (myModule)
{
alert("");
var userId = myModule();
console.log(userId);
});

Systemjs-Builder - Cannot configure properly - Bundling Typescript into a package

I want to build a quick nodejs script to package a Typescript app as SystemJS modules, a lot like what Angular2 bundles look like.
I tried different configurations but I can't seem to put my finger on it, and haven't found clear enough documentation as of yet.
Note that for this "test", I am not using Gulp or Jspm at all, just systemjs-builder for the time being (and don't plan on using jspm at all either)
Here's what my "project" looks like:
---- Project's Root
-------- index.ts // export * from './modules/index' and eventually more
-------- modules
------------ index.ts // export * from './menu/index'
------------ menu
---------------- menu.component.ts // export class
---------------- menu.service.ts // export class
I want to package this under a single file, where I will have multiple SystemRegister modules that can be consumed in an app thereafter
I tried the following without success:
var Builder = require('systemjs-builder');
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('./modules');
builder.bundle('./modules/index.ts', {
/* SystemJS Configuration Here */
baseURL: './modules',
transpiler: 'typescript',
typescriptOptions: {
"module": "system",
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
defaultExtension: 'ts',
packages: {
'modules': {
defaultExtension: 'ts'
}
}
}, 'infrastructure.js')
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.error(err);
})
First of all, the defaultExtension options doesn't seem to work at all
So when I do import {something} from 'filePath'; (without extension), it tries to load filePath, instead of filePath.ts;
Second, if I try adding the .ts extension in my imports (which I don't want to do), it complains that the code is invalid (unexpected token #, unexpected token menuItem and so forth)
Anyone have a good example or some explanations on how this is supposed to work?
Thank you
here you have an example: angular typescript skeleton
build task looks like this:
const path = require('path');
const Builder = require('jspm').Builder;
const builder = new Builder();
const packageJson = require(path.join(config.projectDir, 'package.json'));
return beginBuild()
.then(buildSFX)
.catch((err) => console.log('Build Failed', err));
function beginBuild() {
builder.reset();
return builder.loadConfig(path.join(config.projectDir, packageJson.jspm.configFile))
}
function buildSFX() {
const appName = packageJson.name;
const distFileName = `${appName}.min.js`;
const outFile = path.join(config.distDir, distFileName);
const moduleName = 'app';
const buildConfig = {
format: 'global',
minify: true,
sourceMaps: true
};
return builder.buildStatic(moduleName, outFile, buildConfig);
}
and jspm conf looks like this:
System.config({
defaultJSExtensions: true,
transpiler: "typescript",
typescriptOptions: {
"tsconfig": "src/tsconfig.json"
},
paths: {
"github:*": "vendor/jspm_packages/github/*",
"npm:*": "vendor/jspm_packages/npm/*",
"app": "src/index"
}
/// ...
}
Why do you want to bundle typescript? Bundling is a method used for optimizing the delivery of source code to the browser. The browser doesn't know typescript, it only knows javascript (unless you do on the fly transpiling).

Calling webpacked code from outside (HTML script tag)

Suppose that I have class like this (written in typescript) and I bundle it with webpack into bundle.js.
export class EntryPoint {
static run() {
...
}
}
In my index.html I will include the bundle, but then I would also like to call that static method.
<script src="build/bundle.js"></script>
<script>
window.onload = function() {
EntryPoint.run();
}
</script>
However, the EntryPoint is undefined in this case. How would I call the bundled javascript from another script then?
Added: Webpack config file.
It seems that you want to expose the webpack bundle as a library. You can configure webpack to expose your library in the global context within a variable of your own, like EntryPoint.
I don't know TypeScript so the example uses plain JavaScript instead. But the important piece here is the webpack configuration file, and specifically the output section:
webpack.config.js
module.exports = {
entry: './index.js',
output: {
path: './lib',
filename: 'yourlib.js',
libraryTarget: 'var',
library: 'EntryPoint'
}
};
index.js
module.exports = {
run: function () {
console.log('run from library');
}
};
Then you will be able to access your library methods like you expect:
<script src="lib/yourlib.js"></script>
<script>
window.onload = function () {
EntryPoint.run();
};
</script>
Check the gist with the actual code.
I managed to get this working without any further webpack.config.js modifications, by simply using the import statement which i called from my main/index.js file:
import EntryPoint from './EntryPoint.js';
window.EntryPoint = EntryPoint;
For reference, here's my weback.config.js file.
Initially I tried accomplishing the same using require, however it assigned the module wrapper to window.EntryPoint as opposed to the actual class.
In my circumstance I was able to call a function from within the bundled JavaScript from another script by writing the function to the window when creating it.
// In the bundled script:
function foo() {
var modal = document.createElement('div');
}
// Bind to the window
window.foo = foo;
// Then, in the other script where I want to reference the bundled function I just call it as a normal function
<button onClick="window.foo()">Click Me</button>
I wasn't able to use Babel so this worked for me.
I had a similar challenge, I wanted to create a bundle for multiple pages within a journey and wanted each page to have it's own entry point into the code, and without a separate bundle for each page.
Here's my approach, which is very similar to Kurt Williams but from a slightly different angle, also without changing webpack config:
JourneyMaster.js
import { getViewData } from './modules/common';
import { VIEW_DATA_API_URL } from './modules/constants';
import { createLandingPage, createAnotherPage } from './modules/components/pageBuilder';
window.landingPageInit = () => {
getViewData(VIEW_DATA_API_URL).then(viewData => {
createLandingPage(viewData);
});
};
window.anotherPageInit = () => {
getViewData(VIEW_DATA_API_URL).then(viewData => {
createAnotherPage(viewData);
});
};
// I appreciate the above could be one liners,
// but readable at a glance is important to me
Then an example of how I call these methods at the end of the html page:
<script src="/js/JourneyMaster.js"></script>
<script>window.landingPageInit();</script>
WEBPACK.CONFIG.JS
1.USING UMD
module.exports={
mode:'development',
entry:'./yourentry.js',
output:{
path:path.resolve(__dirname,"dist"),
filename:'main.js',
publicPath:'/dist/',
libraryTarget:'umd',
library:'rstate',
umdNamedDefine: true,
libraryExport: 'default'
}
}
index.html
<script src="dist/main.js"></script>
<script>
window.onload = function () {
rstate()=>{}
</script>
main.js
export default function rstate(){
console.log("i called from html")
}
2.USING VAR
module.exports={
mode:'development',
entry:'./yourentry.js',
output:{
path:path.resolve(__dirname,"dist"),
filename:'main.js',
publicPath:'/dist/',
libraryTarget:'var',
library: 'EntryPoint'
}
}
index.html
<script>
window.onload = function () {
EntryPoint.rstate()=>{}
</script>
main.js
module.exports={
rstate=function(){
console.log("hi module")
}
}
3.USING AMD as library we use like(for those who want to make lib)
define(['jquery', './aux-lib.js'], function ($) { ..(1).. });
Many of the answers so far work, it would only be necessary to clarify that Webpack will not recognize the library until it is built once declared.
You should use npm run build right after creating your library,
before continuing to work with npm start.
At least that's how it works for me, using only webpack.
Maybe this is some impostor syndrome on my part, but I think 'real' coders will cringe at my answer. Regardless, I found this solution to be the best fitting for being pragmatic about my time with my hobby project:
Chane your JS function declaration from:
function renderValue(value) {
to:
global.renderValue = function(value) {
Of course, you'll want to require('path/to/your_custom_js') as you would any file.
I found this answer here:
https://www.fastruby.io/blog/rails/webpack/from-sprockets-to-webpacker.html
This took me forever to figure out as the accepted answer wasn't working for me. Just make sure the function name is the same as the library in the config and it's bundled with the config specified -- npx webpack --config webpack.config.js --mode=development -- hopefully this saves people a few hours.
index.js (function to be bundled) >>
function EntryPoint() {
console.log('called from bundle');
}
module.exports = EntryPoint;
webpack.config.js >>
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'var',
library: 'EntryPoint'
},
};
start.html (where the bundled function is called) >>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Azure SDK Storage Example</title>
<script type="text/javascript" src="./dist/main.js"></script>
</head>
<body>
<h1>Azure SDK Storage Example</h1>
</body>
</html>
<script>
EntryPoint();
</script>
App.ts:
namespace mytypescript.Pages {
export class Manage {
public Initialise() {
$("#btnNewActivity").click(() => {
alert("sdc'");
});
}
}
}
mypage.html:
<input class="button" type="button" id="btnNewActivity" value="Register New Activity" />
<script type="text/javascript">
var page = new mytypescript.Pages.Manage();
page.Initialise();
</script>

Categories