├── .nvmrc ├── .gitignore ├── .prettierrc ├── package.json ├── README.md ├── LICENSE ├── loadConfig.js └── index.js /.nvmrc: -------------------------------------------------------------------------------- 1 | v16.20.1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log* 3 | 4 | .vscode/ 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parcel-transformer-svelte", 3 | "version": "1.2.3", 4 | "description": "Parcel 2 transformer for Svelte 3", 5 | "main": "index.js", 6 | "author": "Vladislav Orlov ", 7 | "repository": "github:orlov-vo/parcel-transformer-svelte", 8 | "license": "MIT", 9 | "keywords": [ 10 | "parcel", 11 | "transformer", 12 | "svelte" 13 | ], 14 | "files": [ 15 | "/index.js", 16 | "/loadConfig.js" 17 | ], 18 | "engines": { 19 | "node": ">=16", 20 | "parcel": "^2.0.0" 21 | }, 22 | "peerDependencies": { 23 | "svelte": "^3.0.0" 24 | }, 25 | "dependencies": { 26 | "@parcel/plugin": "^2.0.0", 27 | "@parcel/source-map": "^2.0.0", 28 | "@parcel/utils": "^2.0.0", 29 | "svelte": "^3.44.0" 30 | }, 31 | "devDependencies": { 32 | "prettier": "^3.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parcel-transformer-svelte 2 | 3 | A Parcel 2 transformer for Svelte 3. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | npm install parcel-transformer-svelte -D 9 | ``` 10 | 11 | After this you should configure in `.parcelrc` the transformation for Svelte files: 12 | 13 | ```json 14 | { 15 | "extends": ["@parcel/config-default"], 16 | "transformers": { 17 | "*.svelte": ["parcel-transformer-svelte"] 18 | } 19 | } 20 | ``` 21 | 22 | ## Configuration 23 | 24 | You can change Svelte options though a `.svelterc`, `svelte.config.js` file or `svelte` field 25 | in `package.json`. 26 | 27 | For documentation on which options you can use look at the official 28 | [svelte docs](https://github.com/sveltejs/svelte). 29 | 30 | ```js 31 | // Options used by svelte.compile 32 | compilerOptions: { 33 | ... 34 | }, 35 | // Preprocessors for svelte.preprocess 36 | preprocessors: { 37 | ... 38 | } 39 | ``` 40 | 41 | ## License 42 | 43 | MIT License 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vladislav Orlov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /loadConfig.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | const CONFIG_FILES = ['.svelterc', 'svelte.config.js']; 6 | const CONFIG_PACKAGE_KEY = 'svelte'; 7 | 8 | async function getConfigFile(config) { 9 | const packageKey = CONFIG_PACKAGE_KEY; 10 | const file = await config.getConfig(CONFIG_FILES, { packageKey }); 11 | 12 | return file || null; 13 | } 14 | 15 | async function configHydrator(configFile, config, options) { 16 | config.setResult({ 17 | raw: configFile, 18 | hydrated: { 19 | preprocess: configFile.preprocess, 20 | }, 21 | }); 22 | } 23 | 24 | exports.load = async function load({ config, options, logger }) { 25 | const configFile = await getConfigFile(config); 26 | if (!configFile) return; 27 | 28 | const { contents } = configFile; 29 | const isDynamic = path.extname(configFile.filePath) === '.js'; 30 | 31 | if (typeof contents !== 'object' && typeof contents !== 'string') { 32 | throw new Error('Svelte config should be an object or a string.'); 33 | } 34 | 35 | if (isDynamic) { 36 | if (!contents.preprocess) { 37 | logger.warn({ 38 | message: 39 | 'WARNING: Using a JavaScript Svelte config file means losing out on caching features of Parcel. Use a .svelterc(.json) file whenever possible.', 40 | }); 41 | } 42 | 43 | config.invalidateOnStartup(); 44 | } 45 | 46 | if (contents.compiler) { 47 | logger.warn({ 48 | message: 49 | 'WARNING: The "compiler" option in .svelterc is deprecated, use "compilerOptions" instead', 50 | }); 51 | contents.compilerOptions = contents.compilerOptions || contents.compiler; 52 | } 53 | 54 | return configHydrator( 55 | { 56 | compilerOptions: { 57 | css: false, 58 | ...contents.compilerOptions, 59 | 60 | dev: options.mode !== 'production', 61 | }, 62 | preprocess: contents.preprocess, 63 | }, 64 | config, 65 | options, 66 | ); 67 | }; 68 | 69 | exports.preSerialize = function preSerialize(config) { 70 | if (!config.result) return; 71 | 72 | // Ensure we don't pass preprocess functions to the serializer 73 | if (config.result.raw.preprocess) { 74 | config.result.raw = {}; 75 | } 76 | 77 | // This gets re-hydrated in Deserialize, so never store this 78 | config.result.hydrated = {}; 79 | }; 80 | 81 | exports.postDeserialize = function postDeserialize(config, options) { 82 | return configHydrator(config.result.raw, config, options); 83 | }; 84 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { Transformer } = require('@parcel/plugin'); 3 | const { default: SourceMap } = require('@parcel/source-map'); 4 | const { relativeUrl } = require('@parcel/utils'); 5 | const { compile, preprocess } = require('svelte/compiler.js'); 6 | const { load, preSerialize, postDeserialize } = require('./loadConfig'); 7 | 8 | Object.defineProperty(exports, '__esModule', { value: true }); 9 | 10 | function generateName(input) { 11 | let name = path 12 | .basename(input) 13 | .replace(path.extname(input), '') 14 | .replace(/[^a-zA-Z_$0-9]+/g, '_') 15 | .replace(/^_/, '') 16 | .replace(/_$/, '') 17 | .replace(/^(\d)/, '_$1'); 18 | 19 | return name[0].toUpperCase() + name.slice(1); 20 | } 21 | 22 | function extractSourceMaps(projectRoot, asset, originalSourceMap, sourceMap) { 23 | if (!sourceMap) return originalSourceMap; 24 | 25 | sourceMap.sources = [asset.filePath]; 26 | 27 | const map = new SourceMap(projectRoot); 28 | map.addVLQMap(sourceMap); 29 | 30 | if (originalSourceMap) { 31 | map.extends(originalSourceMap.toBuffer()); 32 | } 33 | 34 | return map; 35 | } 36 | 37 | async function handleError(sourceFileName, func) { 38 | try { 39 | return await func(); 40 | } catch (error) { 41 | throw new Error(`Error in file ${sourceFileName}: ${error}`); 42 | } 43 | } 44 | 45 | exports.default = new Transformer({ 46 | loadConfig({ config, options, logger }) { 47 | return load({ config, options, logger }); 48 | }, 49 | 50 | preSerializeConfig({ config }) { 51 | return preSerialize(config); 52 | }, 53 | 54 | postDeserializeConfig({ config, options }) { 55 | return postDeserialize(config, options); 56 | }, 57 | 58 | async transform({ asset, config, options }) { 59 | let code = await asset.getCode(); 60 | const originalSourceMap = await asset.getMap(); 61 | 62 | const sourceFileName = relativeUrl(options.projectRoot, asset.filePath); 63 | const compilerOptions = { 64 | ...(config ? config.raw.compilerOptions : null), 65 | filename: sourceFileName, 66 | name: generateName(sourceFileName), 67 | }; 68 | 69 | if (config && config.hydrated.preprocess) { 70 | const preprocessed = await handleError(sourceFileName, () => 71 | preprocess(code, config.hydrated.preprocess, compilerOptions), 72 | ); 73 | code = preprocessed.toString(); 74 | } 75 | 76 | const { js, css } = await handleError(sourceFileName, () => 77 | compile(code, compilerOptions), 78 | ); 79 | 80 | return [ 81 | { 82 | type: 'js', 83 | content: js.code, 84 | uniqueKey: `${asset.id}-js`, 85 | map: extractSourceMaps( 86 | options.projectRoot, 87 | asset, 88 | originalSourceMap, 89 | js.map, 90 | ), 91 | }, 92 | Boolean(css && css.code) && { 93 | type: 'css', 94 | content: css.code, 95 | uniqueKey: `${asset.id}-css`, 96 | map: extractSourceMaps( 97 | options.projectRoot, 98 | asset, 99 | originalSourceMap, 100 | css.map, 101 | ), 102 | }, 103 | ].filter(Boolean); 104 | }, 105 | }); 106 | --------------------------------------------------------------------------------