import Stencil in Svelte - javascript

I have a Monorepo with a svelte project and a Stencil component library. On the Stencil website they very clearly describe how to integrate the library with, for example, Angular
import { defineCustomElements } from 'test-components/loader';
defineCustomElements(window);
Super easy. But now I would like to use it too in a Svelte project ..... not so super easy anymore :(
When I try to do something similar as described above I get serious errors
fbp/dist is where the Stencil files are.
When I build my Stencil project first and copy my dist into the public folder and load ./dist/fbp.js in the head of index.html it all works. But it would be a lot easier if I could include it similar as it does with Angular. Any suggestions?
Update: Added emitCss which gives
Somewhere at the end it stats: Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
UPDATE: With the fixes of #Sambor, Svelte is now able to download the web component, which unfortunately fails

I have created a new project and I manage to reproduce the same problem.
At first, I was thinking is related to typescript and I've tried bunch of plugins in rollup like : #tscc/rollup-plugin-tscc, rollup-plugin-typescript but it didn't work.
I also tried rollup-plugin-amd with same results...
Then I've tried to change the main output format and use es instead of iife.
This way it also required to change the output to a directory instead of file (because of multiple file generation).
And surprisingly this way it seems to work.
here is my code:
/// index.html
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Test</title>
<link rel='stylesheet' href='build/bundle.css'>
<script type="module" defer src='build/main.js'></script>
</head>
<body>
</body>
</html>
Note: main.js is imported as module.
/// main.js
import App from './App.svelte';
import { applyPolyfills, defineCustomElements } from '../my-comp/loader';
applyPolyfills().then(() => {
defineCustomElements(window);
});
const app = new App({ target: document.body });
export default app;
/// rollup.config
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoPreprocess from 'svelte-preprocess';
import json from '#rollup/plugin-json';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'es',
name: 'app',
dir: 'public/build'
},
plugins: [
json(),
svelte({
// Enables run-time checks when not in production.
dev: !production,
// Extracts any component CSS out into a separate file — better for performance.
css: css => css.write('public/build/bundle.css'),
// Emit CSS as "files" for other plugins to process
emitCss: true,
preprocess: autoPreprocess()
}),
resolve({
browser: true,
dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/')
}),
commonjs(),
postcss({
extract: true,
minimize: true,
use: [
['sass', {
includePaths: ['./node_modules']
}]
]
}),
// In dev mode, call `npm run start` once the bundle has been generated
!production && serve(),
// Watches the `public` directory and refresh the browser on changes when not in production.
!production && livereload('public'),
// Minify for production.
production && terser()
],
watch: {
clearScreen: false
}
};
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}
Note: I took my config from another svelte project (you can ignore uninteresting plugins)
Now it seems to be working fine, but I think is just the starting point :) because there are some known issues with stencil itself which I come across;
core-3d1820a5.js:97 TypeError: Failed to fetch dynamically imported module: http://localhost:57231/build/my-component.entry.js
core-3d1820a5.js:863 Uncaught (in promise) TypeError: Cannot read property 'isProxied' of undefined
https://github.com/sveltejs/sapper/issues/464
https://github.com/ionic-team/stencil/issues/1981
same with react: Unable to integrate stenciljs component in React application
This is not the completely working solution, but I thought it may help you for the next steps...

I’m still having the same issue in 2020. Surprisingly, the webpack template is working fine. Switching to that for now, until this is resolved.
https://github.com/sveltejs/template-webpack

Related

Next.js (React) - Can't import local typescript file into config file

Situation
I would like to run some Database code (mongoDB(mongoose)) on server startup / during builds. Considering next js doesn't have any lifecycle hooks that you can hook into in an easy manner, I was trying to perform the database actions in my webpack (next.config.mjs) configuration. However I ran into some problems with importing local files.
Current setup
This is the code of my current next.config.mjs file. (PS. I have also tried the CommonJS way of requiring the needed files, but that also fails with error meessage "module not found".)
None of the lines that import a local typescript file appear to succeed and I have checked the paths multiple times. They always end up with the error message "ERR_MODULE_NOT_FOUND". Only if a node_module package is imported, it works as expected (the mongoose npm package).
Code
/** #type {import('next').NextConfig} */
const { EmployeesSchema } = await import("./mongodb_schemas/employee_schema");
import { EmployeesSchema } from "./mongodb_schemas/employee_schema";
import "./util/test"
import mongoose from "mongoose";
const nextConfig = {
experimental: {
externalDir: true,
},
reactStrictMode: true,
swcMinify: true,
images: {
domains: ["*", "**", "www.google.com"],
},
webpack: (
config,
{ buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }
) => {
if (isServer) {
console.log(process.cwd());
}
return config;
},
};
export default nextConfig;
Anyone got a clue to why this might end up happening / have any possible solutions to the problem? I have also tried with a normal JavaScript file instead of a Typescript file, which also didn't work. I have found some similar asked questions on Stack Overflow but which were all left unanswered.
My guess for the reason why this occurs: during the build of the project, so when "npm run dev" is ran, the next.config.mjs is copied to a different location into the file structure, which means that the relative paths aren't correct anymore and thus the files can't be found.
PS. My apologize if the question is unclear / in an unusual format, it is my first post so not used to it.

Bundle multiple named AMD modules with dependencies into one JS file (building a web app extension system)

I'm working on an extension system for my web app. Third-party developers should be able to extend the app by providing named AMD modules exporting constants and functions following a predefined spec and bundled into a single .js JavaScript file.
Example JavaScript bundle:
define('module1', ['exports', 'module3'], (function (exports, module3) {
exports.spec = 'http://example.com/spec/extension/v1'
exports.onRequest = function (request) { return module3.respond('Hello, World.') }
}));
define('module2', ['exports', 'module3'], (function (exports, module3) {
exports.spec = 'http://example.com/spec/extension/v1'
exports.onRequest = function (request) { return module3.respond('Foo. Bar.') }
}));
define('module3', ['exports'], (function (exports) {
exports.respond = function (message) { return { type: 'message', message: message } }
}));
In the above example module1 and module2 are extension modules (identified by the spec export) and module3 is a shared dependency (e.g. coming from an NPM package). Extension bundles will be loaded in a worker within a sandboxed iframe to seal of the untrusted code in the browser.
Example TypeScript source:
// module1.ts
import respond from 'module3'
export const spec = 'http://example.com/spec/extension/v1'
export const onRequest = (request: Request): Response => respond('Hello, World.')
// module2.ts
import respond from 'module3'
export const spec = 'http://example.com/spec/extension/v1'
export const onRequest = (request: Request): Response => respond('Foo. Bar.')
// module3.ts
import dep from 'some-npm-package'
export respond = (message: string) => dep.createMessageObject(message)
Here is my list of requirements to bundling:
All necessary dependencies (e.g. shared module, NPM package logic) must be included in the bundle
The source code needs to be transpiled to browser compatible code if necessary
The AMD format is required by the custom extension loader implementation
The AMD modules must not be anonymous as the module file names are lost while bundling
No relative paths must be used among dependencies (e.g. ./path/to/module3 instead of module3)
The result should be one JavaScript bundle, thus ONE JavaScript file and ONE sourcemaps file
What's the easiest way to do this?
This is the closest solution I found using rollup and the following rollup.config.js:
import { nodeResolve } from '#rollup/plugin-node-resolve'
import { terser } from 'rollup-plugin-terser'
import typescript from '#rollup/plugin-typescript'
export default {
input: [
'src/module1.ts',
'src/module2.ts'
],
output: {
dir: 'dist',
format: 'amd',
sourcemap: true,
amd: {
autoId: true
}
},
plugins: [
typescript(),
nodeResolve(),
terser()
]
}
From this I get the desired named AMD modules (one for each entry point and chunk) in separate .js files. Problems:
Some dependencies are referenced by ./module3 while being named module3.
The modules appear in separate JavaScript and Sourcemap files instead of being concatenated into a single bundle.
Questions:
Is there an easy fix to the above rollup.config.js config to solve the problem?
I tried to write a small rollup plugin but I failed to get the final AMD module code within it to concatenate it to a bundle. Only the transpiled code is available to me. In addition I don't know how to handle sourcemaps during concatenation.
Is there an alternative to rollup better suited to this bundling scenario?
The big picture: Am I completely on the wrong track when it comes to building an extension system? Is AMD the wrong choice?
I found a way to extend the rollup.config.js mentioned in the question with a custom concatChunks rollup plugin to bundle multiple AMD chunks within a single file and having the source maps rendered, too. The only issue I didn't find an answer to was the relative module names that kept popping up. However, this may be resolved in the AMD loader.
Here's the full rollup.config.js that worked for me:
import Concat from 'concat-with-sourcemaps'
import glob from 'glob'
import typescript from '#rollup/plugin-typescript'
import { nodeResolve } from '#rollup/plugin-node-resolve'
import { terser } from 'rollup-plugin-terser'
const concatChunks = (
fileName = 'bundle.js',
sourceMapFileName = 'bundle.js.map'
) => {
return {
name: 'rollup-plugin-concat-chunks',
generateBundle: function (options, bundle, isWrite) {
const concat = new Concat(true, fileName, '\n')
// Go through each chunk in the bundle
let hasSourceMaps = false
Object.keys(bundle).forEach(fileId => {
const fileInfo = bundle[fileId]
if (fileInfo.type === 'chunk') {
let hasSourceMap = fileInfo.map !== null
hasSourceMaps = hasSourceMaps || hasSourceMap
// Concat file content and source maps with bundle
concat.add(
fileInfo.fileName,
fileInfo.code,
hasSourceMap ? JSON.stringify(fileInfo.map) : null
)
// Prevent single chunks from being emitted
delete bundle[fileId]
}
})
// Emit concatenated chunks
this.emitFile({
type: 'asset',
name: fileName,
fileName: fileName,
source: concat.content
})
// Emit concatenated source maps, if any
if (hasSourceMaps) {
this.emitFile({
type: 'asset',
name: sourceMapFileName,
fileName: sourceMapFileName,
source: concat.sourceMap
})
}
}
}
}
export default {
input: glob.sync('./src/*.{ts,js}'),
output: {
dir: 'dist',
format: 'amd',
sourcemap: true,
amd: {
autoId: true
}
},
plugins: [
typescript(),
nodeResolve(),
terser(),
concatChunks()
]
}
Please make sure you npm install the dependencies referenced in the import statements to make this work.
Considering the big picture, i.e. the extension system itself, I am moving away from a "one AMD module equals one extension/contribution" approach, as current developer tools and JavaScript bundlers are not ready for that (as this question shows). I'll go with an approach similar to the Visual Studio Code Extension API and will use a single "default" module with an activate export to register contributions a bundle has to offer. I hope that this will make extension bundling an easy task no matter what tools or languages are being used.

Vue 3 & Vite built application shows blank page

I have a problem trying to make a build of a new Vue3.js + Vite.js application. Once my application is finished i made the npm run build action in order to generate the final deployment files.
Problem is that when I try to see the generated page, it only shows a white page.
Opening the inspection tool I can see how the main generated javascript files are like not being found by the static index.html:
Failed to load resource: net::ERR_FAILED index.7b66f7af.js:1
Ok. I found the solution searching a bit, and I see how this problem also occurred actually in Vue 2.
The only thing that you have to do for solvif is add base: './' in your vite.config.js, like this:
import {
defineConfig
} from 'vite'
import vue from '#vitejs/plugin-vue'
import vuetify from '#vuetify/vite-plugin'
const path = require('path')
export default defineConfig({
plugins: [
vue(),
vuetify({
autoImport: true,
}),
],
define: {
'process.env': {}
},
resolve: {
alias: {
'#': path.resolve(__dirname, 'src'),
},
},
base: './',
})
Hope it helps you all!
I had this problem also, and found a solution:
It looks like the awnser given by #javi But it's different. I found out that the situation changes when you deploy your application.
In vite config there is a setting called 'base' filled in like: base: mode === 'production' ? '/nameExample/' : '/', this will put the output of your project on the endpoint : 'nameExample'. If you go in production this fails and shows a blank page, and you need to changes this nameExample to '/' to show the projectbuild online. But than it shows a blank page in development because it mismatches the name of the project. Hope this will help you.

Rollup : single html output

I'm trying to package my Svelte app into a single Html file output.
I've managed to get the desired output with a configuration based on that answer :
Output Single HTML File from Svelte Project
With "npm run dev" everything is fine with the first build, but I'm having issues following (live-reload) builds: bundle['bundle.css'] is not filled in my inlineSvelte's generateBundle function.
I didn't manage to change the rollup-plugin-css-only for rollup-plugin-embed-css, which seemed to have an appropriate name for my needs.
Here's my rollup.config.js :
import svelte from 'rollup-plugin-svelte';
import livereload from 'rollup-plugin-livereload';
import css from 'rollup-plugin-css-only';
...
function inlineSvelte(templatePath, dest) {
return {
name: 'Svelte Inliner',
generateBundle(opts, bundle) {
const file = path.parse(opts.file).base;
const jsCode = bundle[file].code;
const cssCode = bundle['bundle.css'].source;
const template = fs.readFileSync(templatePath, 'utf-8');
bundle[file].code = template
.replace('%%script%%', jsCode)
.replace('%%style%%', cssCode);
}
}
}
export default {
input: 'src/main.js',
output: {
format: 'es',
file: outputDir + 'index.html',
name: 'app'
},
plugins: [
svelte({
compilerOptions: {
dev: !production
}
}),
css({ output: 'bundle.css' }),
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
!production && livereload(outputDir),
inlineSvelte('./src/template.html')
],
watch: {
clearScreen: false
}
};
It is surely possible to embed the produced CSS file in your HTML, at least with some reasonably simple custom plugin.
However, if you only have CSS in your Svelte components, that is you don't have import 'whatever.css' anywhere in your code, you can just rely on Svelte injecting CSS from compiled JS code and be done with it.
This loses a little in terms of performance because such injected CSS will never be cached by the browser, but it avoids the added complexity / risk / coupling associated with a custom build step... And this kind of performance is often not so important in scenarios where you want all your app in a single HTML file.
To enable this, set emitCss: false on the Svelte plugin:
plugins: [
svelte({
emitCss: false,
...
}),
...
],
...
You won't need any Rollup plugin for CSS in this case.

How can I get PurifyCSSPlugin to remove my unused css in Angular6?

I'm trying to remove a lot of unused css within my sass files in my Angular 6 project.
I'm learned that there is a webpack plugin called PurifyCss.
Currently now, I'm unable to eject the webpack config in my angular project so I'm using ngw to help add the necessary plugins (to angular's webpack config) needed to extract the unused css in my sass files.
ngw.config.ts
import * as webpack from 'webpack';
import { Path } from '#angular-devkit/core';
import { NormalizedBrowserBuilderSchema } from '#angular-devkit/build-angular';
import * as PurifyCSSPlugin from 'purifycss-webpack';
import * as path from 'path';
import * as glob from 'glob';
export type WebpackOptions<T = NormalizedBrowserBuilderSchema> = {
root: Path,
projectRoot: Path,
options: T;
};
const command = process.argv[2].toLowerCase();
export default function (config: webpack.Configuration, options: WebpackOptions) {
if (command === 'test') {
console.log('Test configuration is running');
}
console.log('To modify webpack build, you can use ngw.config.ts');
console.log('check path:', glob.sync(path.join(__dirname, 'src/**/*.html')));
config.plugins.push(
new PurifyCSSPlugin({
// This was suggested to help it actually remove the css from: https://github.com/webpack-contrib/purifycss-webpack/issues/54
// Although there is an error of: Error: data['resolveExtensions'] should NOT have additional properties
resolveExtensions: ['.html', '.js'],
// This causes the build to run but does not remove the unused css
paths: glob.sync(path.join(__dirname, 'src/**/*.html'))
}),
);
return config;
}
Using the paths property alone doesn't work and it was suggested to add resolveExtensions from here.
Although this leads to the error below when doing a ngw prod build:
Error: data['resolveExtensions'] should NOT have additional properties
How can I configure the PurifyCSSPlugin to remove unused css within sass files in an Angular-6-cli environement?
Note: I don't have much experience with webpack, so I'm not sure if this config only works with css files instead of scss files. (If so please correct me).
Thanks

Categories