UNPKG

32.7 kBJavaScriptView Raw
1"use strict";
2
3const fs = require("fs");
4const rimraf = require("rimraf");
5const { spawnSync } = require("child_process");
6const path = require("path");
7const glob = require("glob");
8const webpack = require("webpack");
9const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
10const CircularDependencyPlugin = require("circular-dependency-plugin");
11
12const Visualizer = require("webpack-visualizer-plugin");
13
14const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
15const FriendlyErrorsWebpackPlugin = require("friendly-errors-webpack-plugin");
16const chalk = require("chalk");
17const NullPlugin = require("webpack-null-plugin");
18const TerserPlugin = require("terser-webpack-plugin");
19const Terser = require("terser");
20const Concat = require("concat-with-sourcemaps");
21const WebpackBuildNotifierPlugin = require("webpack-build-notifier");
22
23const I18nPlugin = require("./tools/i18n-bundler-plugin");
24const runnerArgs = process.argv.slice(2);
25
26/**
27 * Модули, необходимые для сборки частей конфигурации.
28 */
29const requirements = {
30 path: path,
31 glob: glob,
32 rimraf: rimraf
33};
34
35const solutionDir = path.join(".");
36const dist = path.join(solutionDir, "dist");
37
38const createConfig = env => {
39 const transpileOnly = (env && env["transpile-only"]) || false;
40 const visualizeResults = (env && env["visualizeResults"]) || false;
41 const isInProduction = (env && env["production"]) || false;
42 const projects = (env && env["project"]) || null;
43 const noLegacy = (env && env["no-legacy"]) || null;
44
45 const buildContext = {
46 isInProduction: isInProduction,
47 transpileOnly: transpileOnly,
48 visualizeResults: visualizeResults,
49 projects: projects,
50 buildLegacy: !noLegacy
51 };
52
53 return getConfiguration(buildContext);
54};
55
56const testFileMarkers = [
57 "{T,t}ests.ts{,x}",
58 ".spec.ts{,x}",
59 "initialization.ts"
60];
61
62const getTestFiles = (projectPath, testDirectories) => {
63 let result = [];
64
65 for (let testRoot of testDirectories)
66 for (let marker of testFileMarkers) {
67 const testFilesPath = path.join(
68 projectPath,
69 testRoot,
70 `**/*${marker}`
71 );
72 const subResult = glob
73 .sync(testFilesPath)
74 .map(testFile => path.resolve(testFile));
75 result = [...result, ...subResult];
76 }
77
78 return result;
79};
80
81const getDeveloperSettings = () => {
82 const developerSettingsFilePath = path.resolve(
83 __dirname,
84 "./development-settings.json"
85 );
86
87 if (fs.existsSync(developerSettingsFilePath))
88 return require("./development-settings.json");
89};
90
91const resolvePathToAbsolute = (baseDir, targetPath) => {
92 if (path.isAbsolute(targetPath)) return targetPath;
93 else if (baseDir) return path.join(baseDir, targetPath);
94 else
95 throw new Error(
96 "Can't resolve relative path without base dir: " + relativePath
97 );
98};
99
100/**
101 * Получение конфигурации webpack'а для проекта.
102 * @param buildContext - параметры билда
103 * @returns {{}} Конфигурация webpack'а.
104 */
105const getConfiguration = buildContext => {
106 const { isInProduction, projects, buildLegacy } = buildContext;
107
108 if (buildLegacy) compileLegacyProjects(isInProduction);
109
110 const environment = getEnvironmentVariable(isInProduction);
111
112 const configurations = getProjectConfigurationProvidersPaths(projects).map(
113 providerPath => {
114 try {
115 const providerDir = path.dirname(providerPath);
116 const provider = require(providerPath);
117 const providerContext = {
118 projectPath: providerDir,
119 requirements: requirements,
120 environment: environment
121 };
122 const projectConfiguration = provider(providerContext);
123
124 const developerSettings = getDeveloperSettings();
125
126 function resolveDistForModule(name) {
127 if (name && developerSettings && developerSettings.parts)
128 return developerSettings.parts[name];
129 }
130
131 function resolveRelative(relativePath) {
132 if (!relativePath) return undefined;
133
134 if (path.isAbsolute(relativePath))
135 throw new Error(
136 "Path specified in configuration is absolute: " +
137 relativePath
138 );
139
140 return path.join(providerDir, relativePath);
141 }
142
143 let entriesDebugDist = null;
144 let librariesDebugDist = null;
145 let resolveCommonChunkDebug = null;
146
147 const debugDist = resolveDistForModule(
148 projectConfiguration.name
149 );
150 if (debugDist) {
151 if (debugDist.entriesDist)
152 entriesDebugDist = resolvePathToAbsolute(
153 developerSettings.baseDir,
154 debugDist.entriesDist
155 );
156
157 if (debugDist.librariesDist)
158 librariesDebugDist = resolvePathToAbsolute(
159 developerSettings.baseDir,
160 debugDist.librariesDist
161 );
162
163 if (debugDist.commonChunkDist) {
164 const commonChunkDebugDist = resolvePathToAbsolute(
165 developerSettings.baseDir,
166 debugDist.commonChunkDist
167 );
168
169 resolveCommonChunkDebug = filePath =>
170 path.join(
171 commonChunkDebugDist,
172 path.parse(filePath).base
173 );
174 }
175 }
176
177 projectConfiguration.tsconfigPath = resolveRelative(
178 projectConfiguration.tsconfigPath
179 );
180 projectConfiguration.entries = projectConfiguration.entries.map(
181 entry => ({
182 ...entry,
183 outputPath: path.join(
184 entriesDebugDist ||
185 resolveRelative(entry.outputPath),
186 entry.name
187 ),
188 source: resolveRelative(entry.source)
189 })
190 );
191
192 if (projectConfiguration.commonChunk) {
193 projectConfiguration.commonChunk = (
194 resolveCommonChunkDebug || resolveRelative
195 )(projectConfiguration.commonChunk);
196 }
197
198 if (projectConfiguration.testEntries) {
199 projectConfiguration.entries = [
200 ...projectConfiguration.entries,
201 {
202 outputPath: path.join(
203 resolveRelative(
204 projectConfiguration.testEntries.outputPath
205 ),
206 projectConfiguration.testEntries.name
207 ),
208 source: getTestFiles(
209 providerDir,
210 projectConfiguration.testEntries.sourcePaths
211 )
212 }
213 ];
214 }
215
216 if (projectConfiguration.libraries) {
217 projectConfiguration.libraries = projectConfiguration.libraries.map(
218 libraryPack => ({
219 ...libraryPack,
220 outputPath:
221 librariesDebugDist ||
222 resolveRelative(libraryPack.outputPath),
223 files: libraryPack.files
224 .map(f =>
225 Array.isArray(f)
226 ? f[isInProduction ? 1 : 0]
227 : f
228 )
229 .map(resolveRelative)
230 })
231 );
232 }
233
234 if (projectConfiguration.localizationPaths) {
235 projectConfiguration.localizationBundles = {
236 [projectConfiguration.name]: projectConfiguration.localizationPaths.map(
237 resolveRelative
238 )
239 };
240 }
241
242 return {
243 projectConfiguration: projectConfiguration,
244 webpackConfigurationBuilder: configurationTemplateBuilder(
245 buildContext
246 )
247 };
248 } catch (err) {
249 throw `Error processing configuration ${providerPath}: ` + err;
250 }
251 }
252 );
253
254 const projectsMap = {};
255
256 for (let { projectConfiguration } of configurations) {
257 projectsMap[projectConfiguration.name] = projectConfiguration;
258 }
259
260 const rootConfigurations = [];
261 for (let configuration of configurations) {
262 const projectConfiguration = configuration.projectConfiguration;
263
264 if (typeof projectConfiguration.extends === "undefined") {
265 rootConfigurations.push(configuration);
266 continue;
267 }
268
269 const extendedConfiguration = projectsMap[projectConfiguration.extends];
270
271 if (projectConfiguration.entries) {
272 extendedConfiguration.entries = [
273 ...extendedConfiguration.entries,
274 ...projectConfiguration.entries
275 ];
276 }
277
278 if (projectConfiguration.localizationBundles) {
279 extendedConfiguration.localizationBundles = {
280 ...(extendedConfiguration.localizationBundles || {}),
281 ...projectConfiguration.localizationBundles
282 };
283 }
284
285 if (projectConfiguration.libraries) {
286 extendedConfiguration.libraries = [
287 ...(extendedConfiguration.libraries || []),
288 ...projectConfiguration.libraries
289 ];
290 }
291 }
292
293 const entryOutputPaths = rootConfigurations
294 .map(c => c.projectConfiguration.entries.map(entry => entry.outputPath))
295 .reduce((a, b) => [...a, ...b], []);
296
297 new Set(
298 entryOutputPaths
299 .map(entryPath => path.dirname(entryPath))
300 .map(distDir => path.resolve(distDir))
301 .filter(distDir => distDir.includes("dist"))
302 ).forEach(distDir => rimraf.sync(distDir));
303
304 const result = [];
305 for (let {
306 projectConfiguration,
307 webpackConfigurationBuilder
308 } of rootConfigurations) {
309 var configuration = webpackConfigurationBuilder(projectConfiguration);
310 result.push(configuration);
311 if (projectConfiguration.libraries) {
312 bundleLibraries(projectConfiguration.libraries, isInProduction);
313 }
314 }
315
316 getProjectAssetsProvidersPaths(projects).map(providerPath => {
317 try {
318 copyAssets(providerPath);
319 } catch (err) {
320 throw `Error processing assets ${providerPath}: ` + err;
321 }
322 });
323
324 // for debugging final configuration:
325 // fs.writeFileSync(path.resolve(__dirname, "./finalConfig.json"), JSON.stringify(result, null, 2));
326
327 return result;
328};
329
330const getProjectConfigurationProvidersPaths = projects => {
331 return getProjectProvidersPaths(
332 projects,
333 false,
334 configuration => configuration.parts
335 );
336};
337
338const getProjectAssetsProvidersPaths = projects => {
339 return getProjectProvidersPaths(
340 projects,
341 true,
342 configuration => configuration.assets
343 );
344};
345
346const getProjectProvidersPaths = (
347 projects,
348 canSkipProject,
349 configurationPropertyProvider
350) => {
351 const configurationFilePath = path.resolve(__dirname, "./projects.json");
352 if (!fs.existsSync(configurationFilePath))
353 throw new Error(
354 `File with projects list not found in '${configurationFilePath}'`
355 );
356 const availableProjects = Object.entries(
357 configurationPropertyProvider(
358 JSON.parse(fs.readFileSync(configurationFilePath).toString())
359 )
360 );
361 const resultingConfigurationsPaths = new Set();
362 const desiredProjects =
363 projects != null
364 ? typeof projects === "string"
365 ? [projects]
366 : projects
367 : availableProjects.map(ap => ap[0]);
368 for (let desiredProject of desiredProjects) {
369 const project = availableProjects.find(ap => ap[0] === desiredProject);
370 if (project == null)
371 if (canSkipProject) continue;
372 else
373 throw new Error(
374 `Project with name '${desiredProject}' is unknown`
375 );
376 for (let configurationPath of project[1])
377 resultingConfigurationsPaths.add(configurationPath);
378 }
379
380 const result = Array.from(resultingConfigurationsPaths)
381 .map(relativePath => path.join(solutionDir, relativePath))
382 .map(relativePath => path.resolve(relativePath));
383
384 return result;
385};
386
387const configurationTemplateBuilder = buildContext => projectConfiguration => {
388 const { isInProduction, transpileOnly, visualizeResults } = buildContext;
389 const environment = getEnvironmentVariable(isInProduction);
390
391 const {
392 entries,
393 tsconfigPath,
394 commonChunk,
395 localizationBundles
396 } = projectConfiguration;
397
398 const webpackConfigEntry = {};
399 for (let entry of entries) {
400 const entryPath = path.relative(solutionDir, entry.outputPath);
401 webpackConfigEntry[entryPath] = entry.source;
402 }
403
404 const webpackOptimization =
405 typeof commonChunk != "undefined"
406 ? {
407 splitChunks: {
408 cacheGroups: {
409 commons: {
410 name: path.relative(solutionDir, commonChunk),
411 chunks: "initial",
412 minChunks: 2
413 }
414 }
415 }
416 }
417 : undefined;
418
419 const localizationBundlesMap = localizationBundles || {};
420 const developerSettings = getDeveloperSettings();
421
422 let relativeLocalizationTargetDirectory =
423 developerSettings && developerSettings.localizationDist
424 ? path.relative(
425 solutionDir,
426 resolvePathToAbsolute(
427 developerSettings.baseDir,
428 developerSettings.localizationDist
429 )
430 )
431 : path.join(dist, "content", "localization");
432
433 return {
434 mode: "none",
435 devtool: getDevTool(isInProduction),
436 watch: isWatch(),
437 module: {
438 rules: [
439 getTsLoader(tsconfigPath),
440 {
441 test: /\.js$/,
442 exclude: /node_modules/,
443 loader: "source-map-loader",
444 enforce: "pre"
445 },
446
447 // NOTE: Exposes `react` and `react-dom` to use from an external
448 // code.
449 {
450 test: require.resolve("react"),
451 use: [
452 {
453 loader: "expose-loader",
454 options: "React"
455 }
456 ]
457 },
458 {
459 test: require.resolve("react-dom"),
460 use: [
461 {
462 loader: "expose-loader",
463 options: "ReactDOM"
464 }
465 ]
466 },
467
468 {
469 test: /\.scss$/,
470 loaders: [
471 "style-loader",
472 "css-loader",
473 "postcss-loader",
474 "fast-sass-loader"
475 ]
476 },
477 {
478 test: /\.css$/,
479 loaders: ["style-loader", "css-loader", "postcss-loader"]
480 },
481 // Fonts and images would be inlined.
482 {
483 test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
484 loader: "url-loader",
485 options: {
486 limit: 999999
487 }
488 }
489 ]
490 },
491
492 plugins: [
493 ...Object.keys(localizationBundlesMap).map(
494 name =>
495 new I18nPlugin(
496 relativeLocalizationTargetDirectory + "/" + name,
497 localizationBundlesMap[name]
498 )
499 ),
500 getUglifyJsPlugin(isInProduction),
501 new CaseSensitivePathsPlugin(),
502 new CircularDependencyPlugin({
503 exclude: /node_modules/, // Не хотим проверять зависимости в чужих модулях
504 failOnError: true
505 }),
506 // Не хотим импортить все локали для момента.
507 new webpack.ContextReplacementPlugin(
508 /moment[\/\\]locale$/,
509 /ru-ru|en-us/
510 ),
511 new webpack.DefinePlugin({
512 __VERSION__: JSON.stringify(require("../package.json").version),
513 "process.env.NODE_ENV": JSON.stringify(environment)
514 }),
515 getFriendlyErrorsPlugin(),
516 getTsCheckPlugin(isInProduction, transpileOnly, tsconfigPath),
517 getVizualizerPlugin(visualizeResults),
518 new WebpackBuildNotifierPlugin({
519 title: "Frontend Build",
520 suppressSuccess: "always"
521 })
522 ],
523 resolve: {
524 // Нужно уметь резолвить модули из .ts и .tsx
525 extensions: [".ts", ".tsx", ".js", ".json"],
526
527 // Магия вебпака
528 alias: {
529 administration: path.resolve(
530 __dirname,
531 "..",
532 "Administration",
533 "src"
534 ),
535 "administration-tests": path.resolve(
536 __dirname,
537 "..",
538 "Administration",
539 "tests"
540 ),
541 directcrm: path.resolve(__dirname, "..", "DirectCrm", "src"),
542 "directcrm-tests": path.resolve(
543 __dirname,
544 "..",
545 "DirectCrm",
546 "tests"
547 ),
548 mailings: path.resolve(
549 __dirname,
550 "..",
551 "DirectCrm",
552 "Mailings",
553 "src"
554 ),
555 email: path.resolve(
556 __dirname,
557 "..",
558 "DirectCrm",
559 "Mailings",
560 "Email",
561 "src"
562 ),
563 sms: path.resolve(
564 __dirname,
565 "..",
566 "DirectCrm",
567 "Mailings",
568 "Sms",
569 "src"
570 ),
571 viber: path.resolve(
572 __dirname,
573 "..",
574 "DirectCrm",
575 "Mailings",
576 "Viber",
577 "src"
578 ),
579 "mobile-push": path.resolve(
580 __dirname,
581 "..",
582 "DirectCrm",
583 "Mailings",
584 "MobilePush",
585 "src"
586 ),
587 "web-push": path.resolve(
588 __dirname,
589 "..",
590 "DirectCrm",
591 "Mailings",
592 "WebPush",
593 "src"
594 ),
595 retail: path.resolve(
596 __dirname,
597 "..",
598 "DirectCrm",
599 "Retail",
600 "src"
601 ),
602 tracker: path.resolve(
603 __dirname,
604 "..",
605 "Javascript.SDK",
606 "Tracker"
607 ),
608 common: path.resolve(
609 __dirname,
610 "..",
611 "Javascript.SDK",
612 "Common"
613 )
614 }
615 },
616
617 externals: [
618 {
619 react: "React",
620 "react-dom": "ReactDOM",
621 "react-dom-server": "ReactDOMServer",
622 "react-dom/test-utils": "TestUtils",
623 underscore: "_",
624 moment: "moment",
625 redux: "Redux",
626 "react-redux": "ReactRedux",
627 "react-ace": "ReactAce",
628 brace: "ace",
629 "emoji-mart-lite": "EmojiMartLite"
630 }
631 ],
632
633 stats: {
634 timings: true,
635 modules: false,
636 hash: false,
637 version: true,
638
639 // Отрисовывает ошибки плагин
640 errors: false,
641 warnings: false
642 },
643
644 output: {
645 path: path.resolve(solutionDir),
646 filename: "[name].js",
647 devtoolModuleFilenameTemplate: info =>
648 "file:///" +
649 path.resolve(info.absoluteResourcePath).replace(/\\/g, "/"),
650 sourceMapFilename: "[name].js.map"
651 },
652 optimization: webpackOptimization,
653 entry: webpackConfigEntry
654 };
655};
656
657/**
658 * Плагин для красивой отрисовки ошибок.
659 */
660const getFriendlyErrorsPlugin = () => {
661 return new FriendlyErrorsWebpackPlugin({
662 clearConsole: false
663 });
664};
665
666/**
667 * Плагин для проверки типов. Не подключается в режиме транспиляции.
668 */
669const getTsCheckPlugin = (isInProduction, transpileOnly, tsconfigPath) => {
670 if (transpileOnly) {
671 console.log(chalk.blue("Transpile only"));
672 return new NullPlugin();
673 }
674
675 return new ForkTsCheckerWebpackPlugin({
676 checkSyntacticErrors: true,
677 async: false,
678 tslint: getTslintConfigPath(isInProduction),
679 tsconfig: tsconfigPath
680 });
681};
682
683const getVizualizerPlugin = visualizeResults => {
684 if (!visualizeResults) return new NullPlugin();
685
686 console.log(
687 chalk.blue(
688 "Dependencies snapshot will be reported to Webpack.Visualizer.html"
689 )
690 );
691
692 return new Visualizer({
693 filename: "Webpack.Visualizer.html"
694 });
695};
696
697/**
698 * Получение конфигурации сборки
699 * @returns {string} "production" либо "development".
700 */
701const getEnvironmentVariable = isInProduction => {
702 return isInProduction ? "production" : "development";
703};
704
705const getDevTool = isInProduction => {
706 return isInProduction ? "source-map" : "cheap-module-source-map";
707};
708
709const getUglifyJsPlugin = isInProduction => {
710 return isInProduction
711 ? new TerserPlugin({
712 sourceMap: true,
713 parallel: true
714 })
715 : new NullPlugin();
716};
717
718const getTslintConfigPath = isInProduction => {
719 return isInProduction ? "./tslint-production.json" : "./tslint.json";
720};
721
722const isWatch = () => {
723 return runnerArgs.indexOf("--watch") >= 0 || runnerArgs.indexOf("-w") >= 0;
724};
725
726const lintLegacyProjects = () => {
727 const args = [
728 "./node_modules/tslint/bin/tslint",
729 "--project",
730 "./legacy.tsconfig.json",
731 "--config",
732 "./tslint-legacy.json"
733 ];
734
735 const childProcess = spawnSync("node", args);
736
737 if (childProcess.status !== 0) {
738 console.log(chalk.red.bold(childProcess.stdout.toString()));
739
740 console.log(
741 chalk.bgRed.black.bold(" LEGACY TS "),
742 chalk.red(`lint failed with code ${childProcess.status}`)
743 );
744
745 process.exit(childProcess.status);
746 }
747};
748
749/**
750 * Компилирует старые проекты без бандлинга.
751 */
752const compileLegacyProjects = isInProduction => {
753 console.log(
754 chalk.bgYellow.black.bold(" LEGACY TS "),
755 chalk.yellow("build started")
756 );
757
758 const args = [
759 "./node_modules/typescript/bin/tsc",
760 "--project",
761 "./legacy.tsconfig.json",
762 "--noEmitOnError"
763 ];
764
765 if (isWatch()) {
766 console.log(
767 chalk.bgYellow.black.bold(" LEGACY TS "),
768 chalk.yellow("file changes will"),
769 chalk.bgRed.black.bold("!NOT!"),
770 chalk.yellow("be watched")
771 );
772 }
773
774 const childProcess = spawnSync("node", args);
775
776 if (childProcess.status !== 0) {
777 console.log(chalk.red.bold(childProcess.stdout.toString()));
778
779 console.log(
780 chalk.bgRed.black.bold(" LEGACY TS "),
781 chalk.red(`build failed with code ${childProcess.status}`)
782 );
783
784 process.exit(childProcess.status);
785 }
786
787 if (isInProduction) lintLegacyProjects();
788
789 console.log(
790 chalk.bgYellow.black.bold(" LEGACY TS "),
791 chalk.yellow("build finished")
792 );
793};
794
795const minifyPackedLibrary = (name, outputPath, content) => {
796 const library = {
797 [path.join(
798 path.relative(solutionDir, outputPath),
799 name
800 )]: content.toString()
801 };
802
803 let options = {
804 compress: false,
805 mangle: true
806 };
807
808 const minifyResult = Terser.minify(library, options);
809
810 if (minifyResult.error) {
811 throw minifyResult.error;
812 }
813
814 return {
815 content: minifyResult.code
816 };
817};
818
819const concatLibrariesWithoutMinification = (files, bundleFileName) => {
820 const concat = new Concat(true, bundleFileName, "\n");
821 files.forEach(file => {
822 const relativePath = path.relative(solutionDir, file);
823 const content = fs.readFileSync(file).toString();
824
825 const sourceMapPath = file + ".map";
826
827 if (fs.existsSync(sourceMapPath)) {
828 const sourceMapContent = fs.readFileSync(sourceMapPath).toString();
829 const sourceMapReference = content.lastIndexOf(
830 "//# sourceMappingURL="
831 );
832 if (sourceMapReference != -1) {
833 concat.add(
834 relativePath,
835 content.substr(0, sourceMapReference),
836 sourceMapContent
837 );
838 } else {
839 concat.add(relativePath, content, sourceMapContent);
840 }
841 } else {
842 concat.add(relativePath, content);
843 }
844 });
845
846 return {
847 sourceMap: concat.sourceMap,
848 content: concat.content
849 };
850};
851
852const bundleLibraries = (libraries, isInProduction) => {
853 if (libraries == null) return;
854
855 for (let library of libraries) {
856 const { name, outputPath, files } = library;
857 const relativeOutputPath = path.join(
858 ".",
859 path.relative(solutionDir, outputPath)
860 );
861
862 console.log(
863 chalk.bgCyanBright.black(" LIBRARY ") +
864 chalk.reset(" ") + // brightness bug
865 chalk.reset.cyanBright(
866 `"${name}.js" will be assembled in ${relativeOutputPath}`
867 )
868 );
869
870 const bundleFileName = name + ".js";
871 const mapName = name + ".js.map";
872
873 let bundleResult = concatLibrariesWithoutMinification(
874 files,
875 bundleFileName
876 );
877
878 if (!fs.existsSync(outputPath)) {
879 outputPath.split(path.sep).reduce((prevPath, folder) => {
880 const currentPath = path.join(prevPath, folder, path.sep);
881 if (!fs.existsSync(currentPath)) {
882 fs.mkdirSync(currentPath);
883 }
884 return currentPath;
885 }, "");
886 }
887
888 if (isInProduction) {
889 bundleResult = minifyPackedLibrary(
890 name,
891 outputPath,
892 bundleResult.content
893 );
894 fs.writeFileSync(
895 path.join(outputPath, bundleFileName),
896 bundleResult.content
897 );
898 } else {
899 fs.writeFileSync(
900 path.join(outputPath, mapName),
901 bundleResult.sourceMap
902 );
903 fs.writeFileSync(
904 path.join(outputPath, bundleFileName),
905 bundleResult.content.toString() +
906 `\n//# sourceMappingURL=${mapName}\n`
907 );
908 }
909 }
910};
911
912const copyAssets = assetsProviderPath => {
913 const providerDir = path.dirname(assetsProviderPath);
914 const provider = require(assetsProviderPath);
915 const assets = provider();
916
917 if (assets == null) return;
918
919 const developerSettings = getDeveloperSettings();
920
921 function resolveDistForAsset(name) {
922 if (name && developerSettings && developerSettings.assets)
923 return developerSettings.assets[name];
924 }
925
926 for (let asset of assets) {
927 const debugDist = resolveDistForAsset(asset.name);
928
929 const absoluteDestinationDirectory =
930 debugDist && debugDist.destinationDirectory
931 ? resolvePathToAbsolute(
932 developerSettings.baseDir,
933 debugDist.destinationDirectory
934 )
935 : resolvePathToAbsolute(
936 providerDir,
937 asset.destinationDirectory
938 );
939
940 if (!fs.existsSync(absoluteDestinationDirectory)) {
941 absoluteDestinationDirectory
942 .split(path.sep)
943 .reduce((prevPath, folder) => {
944 const currentPath = path.join(prevPath, folder, path.sep);
945 if (!fs.existsSync(currentPath)) {
946 fs.mkdirSync(currentPath);
947 }
948 return currentPath;
949 }, "");
950 }
951
952 for (let sourceFile of asset.sourceFiles) {
953 const absoluteSourceFile = resolvePathToAbsolute(
954 providerDir,
955 sourceFile
956 );
957
958 const fileName = path.parse(absoluteSourceFile).base;
959 const absoluteDestinationFile = path.join(
960 absoluteDestinationDirectory,
961 fileName
962 );
963
964 fs.copyFile(absoluteSourceFile, absoluteDestinationFile, error => {
965 if (error) {
966 throw error;
967 }
968 });
969 }
970 }
971};
972
973const getTsLoader = tsconfigPath => {
974 return {
975 test: /\.tsx?$/,
976 exclude: /node_modules/,
977 use: [
978 {
979 loader: "ts-loader",
980 options: {
981 logLevel: "info",
982 happyPackMode: true,
983 configFile: tsconfigPath
984 }
985 }
986 ]
987 };
988};
989
990module.exports = createConfig;