@playcraft/devkit 1.0.31-beta.1 → 1.0.31-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +262 -0
  2. package/cli/bin/playable-scripts.js +53 -6
  3. package/cli/commands/build.js +241 -32
  4. package/cli/commands/builds.js +13 -2
  5. package/cli/commands/dev.js +22 -1
  6. package/cli/commands/pack.js +323 -41
  7. package/cli/commands/vite-dev.js +7 -1
  8. package/core/artifacts/emit.js +98 -1
  9. package/core/artifacts/fflate.js +31 -1
  10. package/core/artifacts/zip.js +39 -1
  11. package/core/batch-build.js +1261 -149
  12. package/core/constants.js +20 -2
  13. package/core/cos-uploader.js +289 -23
  14. package/core/engines/cocos/clean-cache.js +188 -0
  15. package/core/engines/cocos/clean-cache.test.js +75 -0
  16. package/core/engines/cocos/cli-build.js +116 -2
  17. package/core/engines/cocos/cli.js +38 -1
  18. package/core/engines/cocos/constants.js +21 -1
  19. package/core/engines/cocos/creator-cli.js +108 -1
  20. package/core/engines/cocos/dev-server.js +412 -2
  21. package/core/engines/cocos/fs.js +105 -1
  22. package/core/engines/cocos/injects/3x/init.js +438 -19
  23. package/core/engines/cocos/injects/3x/main.js +52 -2
  24. package/core/engines/cocos/injects/libs/pako.js +2 -1
  25. package/core/engines/cocos/mcp-refresh.js +76 -2
  26. package/core/engines/cocos/merge.js +147 -2
  27. package/core/engines/cocos/pack.js +187 -1
  28. package/core/engines/cocos/preview-cache.js +51 -0
  29. package/core/engines/cocos/preview-cache.test.js +32 -0
  30. package/core/engines/cocos/preview-concat.js +192 -0
  31. package/core/engines/cocos/preview-concat.test.js +73 -0
  32. package/core/engines/cocos/preview-json-hook.js +200 -0
  33. package/core/engines/cocos/preview-json-pack.js +379 -0
  34. package/core/engines/cocos/preview-json-pack.test.js +226 -0
  35. package/core/engines/cocos/preview-proxy.js +132 -0
  36. package/core/engines/cocos/rebundle-preview.js +56 -0
  37. package/core/engines/cocos/strip-social-share.js +115 -3
  38. package/core/engines/cocos/theme-assets.js +161 -2
  39. package/core/engines/cocos/writable-cli.js +250 -0
  40. package/core/engines/cocos/writable-cli.test.js +112 -0
  41. package/core/engines/web/index.js +20 -1
  42. package/core/engines/web/vite.dev.js +322 -17
  43. package/core/engines/web/webpack.build.js +522 -65
  44. package/core/engines/web/webpack.common.js +177 -11
  45. package/core/engines/web/webpack.dev.js +88 -6
  46. package/core/index.d.ts +3 -0
  47. package/core/index.js +101 -7
  48. package/core/injectors/apply.js +94 -1
  49. package/core/injectors/check-channel-matrix.js +130 -2
  50. package/core/injectors/spec.js +217 -9
  51. package/core/loaders/gltf-loader.js +74 -28
  52. package/core/options.js +353 -16
  53. package/core/plugins/AdikteevInjectorPlugin.js +26 -4
  54. package/core/plugins/BigoAdsInjectorPlugin.js +21 -4
  55. package/core/plugins/DAPIInjectorPlugin.js +25 -2
  56. package/core/plugins/DebuggerInjectionPlugin.js +31 -3
  57. package/core/plugins/ExitAPIInjectorPlugin.js +56 -3
  58. package/core/plugins/FflateCompressionPlugin.js +231 -40
  59. package/core/plugins/FontInjectionWebpackPlugin.js +46 -4
  60. package/core/plugins/LiftoffInjectorPlugin.js +26 -4
  61. package/core/plugins/MRAIDInjectorPlugin.js +24 -2
  62. package/core/plugins/MintegralInjectorPlugin.js +25 -2
  63. package/core/plugins/PangleInjectorPlugin.js +24 -2
  64. package/core/plugins/PlaycraftOrientationInjectorPlugin.js +29 -3
  65. package/core/plugins/SnapchatInjectorPlugin.js +26 -4
  66. package/core/plugins/SocialMediaInjectorPlugin.js +119 -7
  67. package/core/plugins/TikTokInjectorPlugin.js +24 -2
  68. package/core/plugins/UnityInjectorPlugin.js +37 -8
  69. package/core/plugins/ZipPlugin.js +154 -20
  70. package/core/plugins/socialMediaVitePlugin.js +108 -2
  71. package/core/utils/buildDefines.js +30 -2
  72. package/core/utils/buildTemplateString.js +39 -5
  73. package/core/utils/date.js +16 -1
  74. package/core/utils/fontRegistry.js +81 -13
  75. package/core/utils/fontResolver.js +335 -37
  76. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +42 -7
  77. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +30 -3
  78. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +43 -7
  79. package/core/utils/getPlaycraftOrientationSuffix.js +9 -2
  80. package/core/utils/injectSDKPlugins.js +68 -13
  81. package/core/utils/logOptions.js +46 -5
  82. package/core/utils/mergeOptions.js +21 -2
  83. package/core/utils/parseArgvOptions.js +125 -5
  84. package/core/utils/resolveChannelFold2Zip.js +15 -3
  85. package/core/utils/validateThemeData.js +227 -26
  86. package/core/validators/index.d.ts +4 -0
  87. package/core/validators/pre-build-checker.js +287 -25
  88. package/core/validators/tracking-validator.js +444 -43
  89. package/core/validators/tracking-validator.test.js +92 -0
  90. package/core/vite.dev.js +1 -1
  91. package/core/webpack.build.js +1 -1
  92. package/core/webpack.common.js +1 -1
  93. package/core/webpack.dev.js +1 -1
  94. package/package.json +3 -2
@@ -1,9 +1,137 @@
1
1
  #!/usr/bin/env node
2
- "use strict";/**
2
+ 'use strict';
3
+
4
+ /**
3
5
  * Compare Cocos pack plan + injections against webpack.build.js rules.
4
6
  * Run: node core/injectors/check-channel-matrix.js
5
7
  *
6
8
  * Webpack is not modified. Known intentional Cocos diffs:
7
9
  * facebook / mintegral zip = inlined index.html only
8
10
  * (webpack still skips HtmlInlineScriptPlugin for those two)
9
- */const{allowedAdNetworks,mraidPartners}=require("../utils/parseArgvOptions");const{applyInjections}=require("./apply");const{getChannelPackPlan,zipOutputNetworks,zipOutputAllowedNetworks}=require("./spec");const WEBPACK_ZIP=["google","pangle","tiktok","vungle","mytarget","mintegral","adikteev","bigabid","inmobi","snapchat","bigoads","liftoff","facebook","remerge"];const WEBPACK_ZIP_OPTIONAL=["moloco"];const WEBPACK_SKIP_INLINE=["mintegral","adikteev","bigabid","facebook"];const INTENTIONAL_INLINE_ZIP=["facebook","mintegral"];function webpackProtocol(network){return mraidPartners.includes(network)?"mraid":"none"}function webpackExpect(network){const zip=WEBPACK_ZIP.includes(network);const skipInline=WEBPACK_SKIP_INLINE.includes(network)&&(zip||network==="mintegral"||network==="adikteev"||network==="bigabid");let files;if(!zip)files=["*.html"];else if(network==="adikteev")files=["index.html","creative.js","style.css"];else if(network==="bigabid")files=["index.html","main.js"];else if(network==="tiktok"||network==="snapchat"||network==="bigoads")files=["index.html","config.json"];else if(skipInline&&network==="facebook")files=["index.html","build.js"];else if(skipInline&&network==="mintegral")files=["index.html","build.js"];else files=["index.html"];return{zip,skipInline,files}}const INJECT_EXPECT={google:["exitapi.js","ad.size","ad.orientation"],tiktok:["union-fe-nc-i18n"],pangle:["union-fe-nc/playable/sdk"],mintegral:["mintGameStart"],unity:["src=\"mraid.js\""],ironsource:["src=\"mraid.js\""],remerge:["src=\"mraid.js\""],liftoff:["src=\"mraid.js\"","Helvetica Neue"],bigoads:["bgy-mraid-sdk.js"],snapchat:["ScPlayableAd"],inmobi:["INMOBI_DSPMACROS"],social_media:["og:type"]};const INJECT_FORBID={applovin:["src=\"mraid.js\""],facebook:["src=\"mraid.js\""],preview:["src=\"mraid.js\""],google:["src=\"mraid.js\""]};const baseHtml="<html><head></head><body><div id=\"GameDiv\"></div></body></html>";const failures=[];const rows=[];for(const network of allowedAdNetworks){const protocol=webpackProtocol(network);const buildOptions={network,protocol,zip:false,googlePlayUrl:"https://play.example",appStoreUrl:"https://app.example",playcraftOrientationValue:"both",filename:"Game"};const themePayload={mergedData:{gamePlay:{shareMeta:{title:"T"}}},i18nOverride:{},assetRecordByPath:{}};const plan=getChannelPackPlan(buildOptions,themePayload);const wx=webpackExpect(network);const html=applyInjections(baseHtml,plan.spec);const cocosFiles=plan.isZip?plan.zipEntries.filter((n,i,a)=>a.indexOf(n)===i):["*.html"];let expectedCocosFiles=wx.files;if(INTENTIONAL_INLINE_ZIP.includes(network))expectedCocosFiles=["index.html"];const zipMismatch=plan.isZip!==wx.zip;const filesMismatch=plan.isZip&&expectedCocosFiles.slice().sort().join()!==cocosFiles.slice().sort().join();for(const s of INJECT_EXPECT[network]||[]){if(!html.includes(s))failures.push(`${network}: missing inject "${s}"`)}for(const s of INJECT_FORBID[network]||[]){if(html.includes(s))failures.push(`${network}: unexpected inject "${s}"`)}if(zipMismatch)failures.push(`${network}: zip webpack=${wx.zip} cocos=${plan.isZip}`);if(filesMismatch)failures.push(`${network}: files webpack/cocos expected=${expectedCocosFiles} got=${cocosFiles}`);if(!html.includes("HandheldFriendly"))failures.push(`${network}: missing recommended meta`);if(!html.includes("FbPlayableAd.onCTAClick"))failures.push(`${network}: missing channel scan stub`);const note=INTENTIONAL_INLINE_ZIP.includes(network)&&wx.files.includes("build.js")?"cocos inlines (webpack still splits)":"";rows.push({network,zip:plan.isZip?"zip":"html",layout:plan.layout,files:cocosFiles.join(", "),note})}if(JSON.stringify(zipOutputNetworks.slice().sort())!==JSON.stringify(WEBPACK_ZIP.slice().sort())){failures.push(`zipOutputNetworks != webpack list`)}if(JSON.stringify(zipOutputAllowedNetworks)!==JSON.stringify(WEBPACK_ZIP_OPTIONAL)){failures.push(`zipOutputAllowedNetworks != webpack list`)}console.log("network".padEnd(14),"fmt".padEnd(6),"layout".padEnd(12),"files");console.log("-".repeat(80));for(const row of rows){console.log(row.network.padEnd(14),row.zip.padEnd(6),row.layout.padEnd(12),row.files,row.note?` ← ${row.note}`:"")}if(failures.length){console.error("\nFAIL");failures.forEach(f=>console.error(" -",f));process.exit(1)}console.log("\nOK all",allowedAdNetworks.length,"networks");console.log("Intentional diffs: facebook, mintegral zip contain only index.html (webpack comment still says split JS).");
11
+ */
12
+
13
+ const { allowedAdNetworks, mraidPartners } = require('../utils/parseArgvOptions');
14
+ const { applyInjections } = require('./apply');
15
+ const { getChannelPackPlan, zipOutputNetworks, zipOutputAllowedNetworks } = require('./spec');
16
+
17
+ const WEBPACK_ZIP = [
18
+ 'google', 'pangle', 'tiktok', 'vungle', 'mytarget', 'mintegral', 'adikteev',
19
+ 'bigabid', 'inmobi', 'snapchat', 'bigoads', 'liftoff', 'facebook', 'remerge'
20
+ ];
21
+ const WEBPACK_ZIP_OPTIONAL = ['moloco'];
22
+ const WEBPACK_SKIP_INLINE = ['mintegral', 'adikteev', 'bigabid', 'facebook'];
23
+ const INTENTIONAL_INLINE_ZIP = ['facebook', 'mintegral'];
24
+
25
+ function webpackProtocol(network) {
26
+ return mraidPartners.includes(network) ? 'mraid' : 'none';
27
+ }
28
+
29
+ function webpackExpect(network) {
30
+ const zip = WEBPACK_ZIP.includes(network);
31
+ const skipInline = WEBPACK_SKIP_INLINE.includes(network) && (zip || network === 'mintegral' || network === 'adikteev' || network === 'bigabid');
32
+ let files;
33
+ if (!zip) files = ['*.html'];
34
+ else if (network === 'adikteev') files = ['index.html', 'creative.js', 'style.css'];
35
+ else if (network === 'bigabid') files = ['index.html', 'main.js'];
36
+ else if (network === 'tiktok' || network === 'snapchat' || network === 'bigoads') files = ['index.html', 'config.json'];
37
+ else if (skipInline && network === 'facebook') files = ['index.html', 'build.js'];
38
+ else if (skipInline && network === 'mintegral') files = ['index.html', 'build.js'];
39
+ else files = ['index.html'];
40
+ return { zip, skipInline, files };
41
+ }
42
+
43
+ const INJECT_EXPECT = {
44
+ google: ['exitapi.js', 'ad.size', 'ad.orientation'],
45
+ tiktok: ['union-fe-nc-i18n'],
46
+ pangle: ['union-fe-nc/playable/sdk'],
47
+ mintegral: ['mintGameStart'],
48
+ unity: ['src="mraid.js"'],
49
+ ironsource: ['src="mraid.js"'],
50
+ remerge: ['src="mraid.js"'],
51
+ liftoff: ['src="mraid.js"', 'Helvetica Neue'],
52
+ bigoads: ['bgy-mraid-sdk.js'],
53
+ snapchat: ['ScPlayableAd'],
54
+ inmobi: ['INMOBI_DSPMACROS'],
55
+ social_media: ['og:type'],
56
+ };
57
+
58
+ const INJECT_FORBID = {
59
+ applovin: ['src="mraid.js"'],
60
+ facebook: ['src="mraid.js"'],
61
+ preview: ['src="mraid.js"'],
62
+ google: ['src="mraid.js"'],
63
+ };
64
+
65
+ const baseHtml = '<html><head></head><body><div id="GameDiv"></div></body></html>';
66
+ const failures = [];
67
+ const rows = [];
68
+
69
+ for (const network of allowedAdNetworks) {
70
+ const protocol = webpackProtocol(network);
71
+ const buildOptions = {
72
+ network,
73
+ protocol,
74
+ zip: false,
75
+ googlePlayUrl: 'https://play.example',
76
+ appStoreUrl: 'https://app.example',
77
+ playcraftOrientationValue: 'both',
78
+ filename: 'Game',
79
+ };
80
+ const themePayload = { mergedData: { gamePlay: { shareMeta: { title: 'T' } } }, i18nOverride: {}, assetRecordByPath: {} };
81
+ const plan = getChannelPackPlan(buildOptions, themePayload);
82
+ const wx = webpackExpect(network);
83
+ const html = applyInjections(baseHtml, plan.spec);
84
+
85
+ const cocosFiles = plan.isZip
86
+ ? plan.zipEntries.filter((n, i, a) => a.indexOf(n) === i)
87
+ : ['*.html'];
88
+
89
+ let expectedCocosFiles = wx.files;
90
+ if (INTENTIONAL_INLINE_ZIP.includes(network)) expectedCocosFiles = ['index.html'];
91
+
92
+ const zipMismatch = plan.isZip !== wx.zip;
93
+ const filesMismatch = plan.isZip && expectedCocosFiles.slice().sort().join() !== cocosFiles.slice().sort().join();
94
+
95
+ for (const s of INJECT_EXPECT[network] || []) {
96
+ if (!html.includes(s)) failures.push(`${network}: missing inject "${s}"`);
97
+ }
98
+ for (const s of INJECT_FORBID[network] || []) {
99
+ if (html.includes(s)) failures.push(`${network}: unexpected inject "${s}"`);
100
+ }
101
+ if (zipMismatch) failures.push(`${network}: zip webpack=${wx.zip} cocos=${plan.isZip}`);
102
+ if (filesMismatch) failures.push(`${network}: files webpack/cocos expected=${expectedCocosFiles} got=${cocosFiles}`);
103
+ if (!html.includes('HandheldFriendly')) failures.push(`${network}: missing recommended meta`);
104
+ if (!html.includes('FbPlayableAd.onCTAClick')) failures.push(`${network}: missing channel scan stub`);
105
+
106
+ const note = INTENTIONAL_INLINE_ZIP.includes(network) && wx.files.includes('build.js')
107
+ ? 'cocos inlines (webpack still splits)'
108
+ : '';
109
+ rows.push({
110
+ network,
111
+ zip: plan.isZip ? 'zip' : 'html',
112
+ layout: plan.layout,
113
+ files: cocosFiles.join(', '),
114
+ note,
115
+ });
116
+ }
117
+
118
+ if (JSON.stringify(zipOutputNetworks.slice().sort()) !== JSON.stringify(WEBPACK_ZIP.slice().sort())) {
119
+ failures.push(`zipOutputNetworks != webpack list`);
120
+ }
121
+ if (JSON.stringify(zipOutputAllowedNetworks) !== JSON.stringify(WEBPACK_ZIP_OPTIONAL)) {
122
+ failures.push(`zipOutputAllowedNetworks != webpack list`);
123
+ }
124
+
125
+ console.log('network'.padEnd(14), 'fmt'.padEnd(6), 'layout'.padEnd(12), 'files');
126
+ console.log('-'.repeat(80));
127
+ for (const row of rows) {
128
+ console.log(row.network.padEnd(14), row.zip.padEnd(6), row.layout.padEnd(12), row.files, row.note ? ` ← ${row.note}` : '');
129
+ }
130
+
131
+ if (failures.length) {
132
+ console.error('\nFAIL');
133
+ failures.forEach((f) => console.error(' -', f));
134
+ process.exit(1);
135
+ }
136
+ console.log('\nOK all', allowedAdNetworks.length, 'networks');
137
+ console.log('Intentional diffs: facebook, mintegral zip contain only index.html (webpack comment still says split JS).');
@@ -1,10 +1,34 @@
1
- "use strict";const path=require("path");const{buildDefines}=require("../utils/buildDefines");const META={COMMON_CHARSET:"utf-8",COMMON_VIEWPORT:"width=device-width,initial-scale=1.0,viewport-fit=cover,maximum-scale=1.0,user-scalable=no",MINTEGRAL_VIEWPORT:"width=device-width,user-scalable=no,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"};const LIFTOFF_FONT="<style>body{font-family:Roboto,\"Helvetica Neue\",-apple-system,BlinkMacSystemFont,sans-serif}</style>";const zipOutputNetworks=["google","pangle","tiktok","vungle","mytarget","mintegral","adikteev","bigabid","inmobi","snapchat","bigoads","liftoff","facebook","remerge"];const zipOutputAllowedNetworks=["moloco"];/** Only platforms that require a separate JS file on CDN. Facebook / mintegral zip = single inlined index.html. */const splitJsNetworks=["adikteev","bigabid"];const ADIKTEEV_INDEX=`<meta charset="UTF-8">
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { buildDefines } = require('../utils/buildDefines');
5
+
6
+ const META = {
7
+ COMMON_CHARSET: 'utf-8',
8
+ COMMON_VIEWPORT: 'width=device-width,initial-scale=1.0,viewport-fit=cover,maximum-scale=1.0,user-scalable=no',
9
+ MINTEGRAL_VIEWPORT: 'width=device-width,user-scalable=no,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0',
10
+ };
11
+
12
+ const LIFTOFF_FONT = '<style>body{font-family:Roboto,"Helvetica Neue",-apple-system,BlinkMacSystemFont,sans-serif}</style>';
13
+
14
+ const zipOutputNetworks = [
15
+ 'google', 'pangle', 'tiktok', 'vungle', 'mytarget', 'mintegral', 'adikteev',
16
+ 'bigabid', 'inmobi', 'snapchat', 'bigoads', 'liftoff', 'facebook', 'remerge'
17
+ ];
18
+ const zipOutputAllowedNetworks = ['moloco'];
19
+
20
+ /** Only platforms that require a separate JS file on CDN. Facebook / mintegral zip = single inlined index.html. */
21
+ const splitJsNetworks = ['adikteev', 'bigabid'];
22
+
23
+ const ADIKTEEV_INDEX = `<meta charset="UTF-8">
2
24
  <link rel="stylesheet" href="style.css">
3
25
  <script type="application/javascript">
4
26
  var AK_CLICK_DESTINATION_URL = "PLACEHOLDER_CLICK_REDIRECT";
5
27
  var AK_CLICK_PIXEL_URL = "PLACEHOLDER_CLICK_PIXEL";
6
28
  </script>
7
- <script type="application/javascript" src="creative.js"></script>`;const BIGABID_INDEX=`<meta charset="UTF-8">
29
+ <script type="application/javascript" src="creative.js"></script>`;
30
+
31
+ const BIGABID_INDEX = `<meta charset="UTF-8">
8
32
  <script src="mraid.js"></script>
9
33
  <script>window.BIGABID_BIDTIMEMACROS = {
10
34
  mraid_viewable: unescape("#IMP_TRACE_MRAID_VIEWABLE_ESC#"),
@@ -15,7 +39,45 @@
15
39
  final_url: unescape("#FINAL_LANDING_URL_ESC#")
16
40
  }</script>
17
41
  <script src="#BIGABID_PLAYABLE_CDN_URL#"></script>
18
- %{IMP_BEACON}`;const dapiSrc="function getScript(e,i){var n=document.createElement(\"script\");n.type=\"text/javascript\",n.async=!0,i&&(n.onload=i),n.src=e,document.head.appendChild(n)}function parseMessage(e){var i=e.data,n=i.indexOf(DOLLAR_PREFIX+RECEIVE_MSG_PREFIX);if(-1!==n){var t=i.slice(n+2);return getMessageParams(t)}return{}}function getMessageParams(e){var i,n=[],t=e.split(\"/\"),a=t.length;if(-1===e.indexOf(RECEIVE_MSG_PREFIX)){if(a>=2&&a%2===0)for(i=0;a>i;i+=2)n[t[i]]=t.length<i+1?null:decodeURIComponent(t[i+1])}else{var o=e.split(RECEIVE_MSG_PREFIX);void 0!==o[1]&&(n=JSON&&JSON.parse(o[1]))}return n}function getDapi(e){var i=parseMessage(e);if(!i||i.name===GET_DAPI_URL_MSG_NAME){var n=i.data;getScript(n,onDapiReceived)}}function invokeDapiListeners(){for(var e in dapiEventsPool)dapiEventsPool.hasOwnProperty(e)&&dapi.addEventListener(e,dapiEventsPool[e])}function onDapiReceived(){dapi=window.dapi,window.removeEventListener(\"message\",getDapi),invokeDapiListeners()}function init(){window.dapi.isDemoDapi&&(window.parent.postMessage(DOLLAR_PREFIX+SEND_MSG_PREFIX+JSON.stringify({state:\"getDapiUrl\"}),\"*\"),window.addEventListener(\"message\",getDapi,!1))}var DOLLAR_PREFIX=\"$$\",RECEIVE_MSG_PREFIX=\"DAPI_SERVICE:\",SEND_MSG_PREFIX=\"DAPI_AD:\",GET_DAPI_URL_MSG_NAME=\"connection.getDapiUrl\",dapiEventsPool={},dapi=window.dapi||{isReady:function(){return!1},addEventListener:function(e,i){dapiEventsPool[e]=i},removeEventListener:function(e){delete dapiEventsPool[e]},isDemoDapi:!0};init();";const mintegralSrc="function gameStart() {window.mintGameStart && window.mintGameStart()}; function gameClose() {window.mintGameClose && window.mintGameClose()}";const RESOURCES=path.join(__dirname,"../resources");function scriptSrc(src){return{tagName:"script",attributes:{src,type:"text/javascript"}}}function scriptInline(innerHTML){return{tagName:"script",attributes:{type:"text/javascript"},innerHTML}}function metaTag(attrs){return{tagName:"meta",attributes:attrs}}function buildWindowScript(buildOptions,themePayload){const defines=buildDefines(buildOptions);const lines=Object.entries(defines).map(([key,value])=>`window.${key}=${value};`);const orientation=JSON.stringify(buildOptions.playcraftOrientationValue||"both");lines.push(`window.playcraftOrientation=${orientation};`);if(themePayload){lines.push(`window.__PLAYCRAFT_THEME_DATA__=${JSON.stringify(themePayload.mergedData||{})};`);lines.push(`window.__PLAYCRAFT_THEME_I18N_OVERRIDE__=${JSON.stringify(themePayload.i18nOverride||{})};`);lines.push(`window.__PLAYCRAFT_THEME_ASSETS__=${JSON.stringify(themePayload.assetRecordByPath||{})};`)}return lines.join("")}function buildChannelScanStubHtml(buildOptions={}){const googlePlayUrl=buildOptions.googlePlayUrl||"https://play.google.com/store/games";const appStoreUrl=buildOptions.appStoreUrl||"https://www.apple.com/app-store/";return`
42
+ %{IMP_BEACON}`;
43
+
44
+ const dapiSrc =
45
+ 'function getScript(e,i){var n=document.createElement("script");n.type="text/javascript",n.async=!0,i&&(n.onload=i),n.src=e,document.head.appendChild(n)}function parseMessage(e){var i=e.data,n=i.indexOf(DOLLAR_PREFIX+RECEIVE_MSG_PREFIX);if(-1!==n){var t=i.slice(n+2);return getMessageParams(t)}return{}}function getMessageParams(e){var i,n=[],t=e.split("/"),a=t.length;if(-1===e.indexOf(RECEIVE_MSG_PREFIX)){if(a>=2&&a%2===0)for(i=0;a>i;i+=2)n[t[i]]=t.length<i+1?null:decodeURIComponent(t[i+1])}else{var o=e.split(RECEIVE_MSG_PREFIX);void 0!==o[1]&&(n=JSON&&JSON.parse(o[1]))}return n}function getDapi(e){var i=parseMessage(e);if(!i||i.name===GET_DAPI_URL_MSG_NAME){var n=i.data;getScript(n,onDapiReceived)}}function invokeDapiListeners(){for(var e in dapiEventsPool)dapiEventsPool.hasOwnProperty(e)&&dapi.addEventListener(e,dapiEventsPool[e])}function onDapiReceived(){dapi=window.dapi,window.removeEventListener("message",getDapi),invokeDapiListeners()}function init(){window.dapi.isDemoDapi&&(window.parent.postMessage(DOLLAR_PREFIX+SEND_MSG_PREFIX+JSON.stringify({state:"getDapiUrl"}),"*"),window.addEventListener("message",getDapi,!1))}var DOLLAR_PREFIX="$$",RECEIVE_MSG_PREFIX="DAPI_SERVICE:",SEND_MSG_PREFIX="DAPI_AD:",GET_DAPI_URL_MSG_NAME="connection.getDapiUrl",dapiEventsPool={},dapi=window.dapi||{isReady:function(){return!1},addEventListener:function(e,i){dapiEventsPool[e]=i},removeEventListener:function(e){delete dapiEventsPool[e]},isDemoDapi:!0};init();';
46
+
47
+ const mintegralSrc =
48
+ 'function gameStart() {window.mintGameStart && window.mintGameStart()}; function gameClose() {window.mintGameClose && window.mintGameClose()}';
49
+
50
+ const RESOURCES = path.join(__dirname, '../resources');
51
+
52
+ function scriptSrc(src) {
53
+ return { tagName: 'script', attributes: { src, type: 'text/javascript' } };
54
+ }
55
+
56
+ function scriptInline(innerHTML) {
57
+ return { tagName: 'script', attributes: { type: 'text/javascript' }, innerHTML };
58
+ }
59
+
60
+ function metaTag(attrs) {
61
+ return { tagName: 'meta', attributes: attrs };
62
+ }
63
+
64
+ function buildWindowScript(buildOptions, themePayload) {
65
+ const defines = buildDefines(buildOptions);
66
+ const lines = Object.entries(defines).map(([key, value]) => `window.${key}=${value};`);
67
+ const orientation = JSON.stringify(buildOptions.playcraftOrientationValue || 'both');
68
+ lines.push(`window.playcraftOrientation=${orientation};`);
69
+ if (themePayload) {
70
+ lines.push(`window.__PLAYCRAFT_THEME_DATA__=${JSON.stringify(themePayload.mergedData || {})};`);
71
+ lines.push(`window.__PLAYCRAFT_THEME_I18N_OVERRIDE__=${JSON.stringify(themePayload.i18nOverride || {})};`);
72
+ lines.push(`window.__PLAYCRAFT_THEME_ASSETS__=${JSON.stringify(themePayload.assetRecordByPath || {})};`);
73
+ }
74
+ return lines.join('');
75
+ }
76
+
77
+ function buildChannelScanStubHtml(buildOptions = {}) {
78
+ const googlePlayUrl = buildOptions.googlePlayUrl || 'https://play.google.com/store/games';
79
+ const appStoreUrl = buildOptions.appStoreUrl || 'https://www.apple.com/app-store/';
80
+ return `
19
81
  <script>
20
82
  (function() {
21
83
  // 为了通过渠道的检测,伪造几个对象和方法 FbPlayableAd.onCTAClick(); ExitApi.exit();window.BGY_MRAID.open() mraid.open(destinationUrl); dapi.openStoreUrl(); NUC.trigger.convert(destinationUrl)
@@ -67,7 +129,10 @@
67
129
  : ${JSON.stringify(googlePlayUrl)};
68
130
  mraid.open(destinationUrl);
69
131
  }
70
- </script>`}const SNAPCHAT_MOCK=`
132
+ </script>`;
133
+ }
134
+
135
+ const SNAPCHAT_MOCK = `
71
136
  // Snapchat Playable Ad SDK Mock (开发环境)
72
137
  if (typeof ScPlayableAd === 'undefined') {
73
138
  window.ScPlayableAd = {
@@ -80,14 +145,47 @@ if (typeof ScPlayableAd === 'undefined') {
80
145
  };
81
146
  console.log('[Snapchat] SDK initialized (development mode)');
82
147
  }
83
- `.trim();function hasScriptSrc(headTags,src){return headTags.some(tag=>tag.tagName==="script"&&tag.attributes&&tag.attributes.src===src)}function buildShareMetaHtml(shareMeta){if(!shareMeta)return"";const esc=s=>String(s).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;");const title=(shareMeta.title||"").trim();const description=(shareMeta.description||"").trim();const image=(shareMeta.imageUrl||"").trim();const url=(shareMeta.pageUrl||"").trim();const siteName=(shareMeta.siteName||"").trim();const lines=[];if(title)lines.push(`<meta property="og:title" content="${esc(title)}" />`);if(description)lines.push(`<meta property="og:description" content="${esc(description)}" />`);if(image)lines.push(`<meta property="og:image" content="${esc(image)}" />`);if(url)lines.push(`<meta property="og:url" content="${esc(url)}" />`);if(siteName)lines.push(`<meta property="og:site_name" content="${esc(siteName)}" />`);lines.push("<meta property=\"og:type\" content=\"website\" />");lines.push("<meta name=\"twitter:card\" content=\"summary_large_image\" />");if(title)lines.push(`<meta name="twitter:title" content="${esc(title)}" />`);if(description)lines.push(`<meta name="twitter:description" content="${esc(description)}" />`);if(image)lines.push(`<meta name="twitter:image" content="${esc(image)}" />`);return lines.join("\n ")}function buildInmobiScript(buildOptions){return`<script>
148
+ `.trim();
149
+
150
+ function hasScriptSrc(headTags, src) {
151
+ return headTags.some((tag) => tag.tagName === 'script' && tag.attributes && tag.attributes.src === src);
152
+ }
153
+
154
+ function buildShareMetaHtml(shareMeta) {
155
+ if (!shareMeta) return '';
156
+ const esc = (s) => String(s)
157
+ .replace(/&/g, '&amp;')
158
+ .replace(/"/g, '&quot;')
159
+ .replace(/</g, '&lt;')
160
+ .replace(/>/g, '&gt;');
161
+ const title = (shareMeta.title || '').trim();
162
+ const description = (shareMeta.description || '').trim();
163
+ const image = (shareMeta.imageUrl || '').trim();
164
+ const url = (shareMeta.pageUrl || '').trim();
165
+ const siteName = (shareMeta.siteName || '').trim();
166
+ const lines = [];
167
+ if (title) lines.push(`<meta property="og:title" content="${esc(title)}" />`);
168
+ if (description) lines.push(`<meta property="og:description" content="${esc(description)}" />`);
169
+ if (image) lines.push(`<meta property="og:image" content="${esc(image)}" />`);
170
+ if (url) lines.push(`<meta property="og:url" content="${esc(url)}" />`);
171
+ if (siteName) lines.push(`<meta property="og:site_name" content="${esc(siteName)}" />`);
172
+ lines.push('<meta property="og:type" content="website" />');
173
+ lines.push('<meta name="twitter:card" content="summary_large_image" />');
174
+ if (title) lines.push(`<meta name="twitter:title" content="${esc(title)}" />`);
175
+ if (description) lines.push(`<meta name="twitter:description" content="${esc(description)}" />`);
176
+ if (image) lines.push(`<meta name="twitter:image" content="${esc(image)}" />`);
177
+ return lines.join('\n ');
178
+ }
179
+
180
+ function buildInmobiScript(buildOptions) {
181
+ return `<script>
84
182
  var INMOBI_DSPMACROS = {
85
183
  "Ad_Load_Start": ["$P_AD_LOAD_START"],
86
184
  "Ad_Viewable": ["$P_AD_READY"],
87
185
  "First_Engagement": ["$P_FIRST_ENGAGEMENT"],
88
186
  "Gameplay_Complete": ["$P_GAMEPLAY_DONE"],
89
187
  "DSP_Click": ["$HTML_ESC_CLICK_URL"],
90
- "LandingPage_URL": [/android/i.test(navigator.userAgent) ? ${JSON.stringify(buildOptions.googlePlayUrl||"")} : ${JSON.stringify(buildOptions.appStoreUrl||"")}],
188
+ "LandingPage_URL": [/android/i.test(navigator.userAgent) ? ${JSON.stringify(buildOptions.googlePlayUrl || '')} : ${JSON.stringify(buildOptions.appStoreUrl || '')}],
91
189
  "Spent_5_Seconds": ["$P_TIMESPENT_5"],
92
190
  "Spent_10_Seconds": ["$P_TIMESPENT_10"],
93
191
  "Spent_15_Seconds": ["$P_TIMESPENT_15"],
@@ -96,12 +194,122 @@ if (typeof ScPlayableAd === 'undefined') {
96
194
  "Spent_30_Seconds": ["$P_TIMESPENT_30"]
97
195
  };
98
196
  </script>
99
- <script src="mraid.js"></script>`}/**
197
+ <script src="mraid.js"></script>`;
198
+ }
199
+
200
+ /**
100
201
  * Mirror injectSDKPlugins.js + webpack.build.js extra files / meta.
101
202
  * Webpack plugins stay untouched; Cocos apply.js consumes this spec.
102
- */function getChannelInjectionSpec(buildOptions,themePayload){const network=buildOptions.network;const protocol=buildOptions.protocol;const orientation=buildOptions.playcraftOrientationValue||"both";const headTags=[];const htmlTransforms=[];const extraFiles=[];headTags.push(scriptInline(buildWindowScript(buildOptions,themePayload)));if(protocol==="dapi"){headTags.push(scriptInline(dapiSrc))}else if(protocol==="mraid"&&(network==="ironsource"||network==="remerge")){headTags.push(scriptSrc("mraid.js"))}if(network==="google"){const width=orientation==="landscape"?480:320;const height=orientation==="landscape"?320:480;headTags.push(metaTag({name:"ad.orientation",content:orientation==="both"?"portrait,landscape":orientation}));headTags.push(metaTag({name:"ad.size",content:`width=${width},height=${height}`}));headTags.push(scriptSrc("https://tpc.googlesyndication.com/pagead/gadgets/html5/api/exitapi.js"))}else if(network==="unity"){if(!hasScriptSrc(headTags,"mraid.js"))headTags.push(scriptSrc("mraid.js"));htmlTransforms.push(html=>html.replace(/window\.top\b/g,"window"))}else if(network==="pangle"){headTags.push(scriptSrc("https://sf3-ttcdn-tos.pstatp.com/obj/union-fe-nc/playable/sdk/playable-sdk.js"))}else if(network==="tiktok"){headTags.push(scriptSrc("https://sf16-muse-va.ibytedtos.com/obj/union-fe-nc-i18n/playable/sdk/playable-sdk.js"))}else if(network==="mintegral"){headTags.push(scriptInline(mintegralSrc))}else if(network==="bigoads"){headTags.push(scriptSrc("https://static-web.likeevideo.com/as/common-static/big-data/dsp-public/bgy-mraid-sdk.js"))}else if(network==="liftoff"){if(!hasScriptSrc(headTags,"mraid.js"))headTags.push(scriptSrc("mraid.js"))}else if(network==="snapchat"){headTags.push(scriptInline(SNAPCHAT_MOCK))}else if(network==="inmobi"){htmlTransforms.push(html=>html.replace(/<\/head>/i,`${buildInmobiScript(buildOptions)}\n</head>`))}else if(network==="social_media"){const shareMeta=themePayload&&themePayload.mergedData&&themePayload.mergedData.gamePlay?themePayload.mergedData.gamePlay.shareMeta:null;const tags=buildShareMetaHtml(shareMeta);if(tags){htmlTransforms.push(html=>html.replace(/<\/head>/i,` ${tags}\n </head>`))}}if(network==="tiktok"){extraFiles.push({from:path.join(RESOURCES,"tiktok-config.json"),to:"config.json"})}else if(network==="snapchat"){extraFiles.push({from:path.join(RESOURCES,"snapchat-config.json"),to:"config.json"})}else if(network==="bigoads"){extraFiles.push({from:path.join(RESOURCES,"bigoads-config.json"),to:"config.json"})}return{network,headTags,htmlTransforms,extraFiles,viewport:network==="mintegral"?META.MINTEGRAL_VIEWPORT:META.COMMON_VIEWPORT,skipRecommendedMeta:!!buildOptions.skipRecommendedMeta,liftoffFont:network==="liftoff",scanStubHtml:buildChannelScanStubHtml(buildOptions)}}/**
203
+ */
204
+ function getChannelInjectionSpec(buildOptions, themePayload) {
205
+ const network = buildOptions.network;
206
+ const protocol = buildOptions.protocol;
207
+ const orientation = buildOptions.playcraftOrientationValue || 'both';
208
+ const headTags = [];
209
+ const htmlTransforms = [];
210
+ const extraFiles = [];
211
+
212
+ headTags.push(scriptInline(buildWindowScript(buildOptions, themePayload)));
213
+
214
+ if (protocol === 'dapi') {
215
+ headTags.push(scriptInline(dapiSrc));
216
+ } else if (protocol === 'mraid' && (network === 'ironsource' || network === 'remerge')) {
217
+ headTags.push(scriptSrc('mraid.js'));
218
+ }
219
+
220
+ if (network === 'google') {
221
+ const width = orientation === 'landscape' ? 480 : 320;
222
+ const height = orientation === 'landscape' ? 320 : 480;
223
+ headTags.push(metaTag({ name: 'ad.orientation', content: orientation === 'both' ? 'portrait,landscape' : orientation }));
224
+ headTags.push(metaTag({ name: 'ad.size', content: `width=${width},height=${height}` }));
225
+ headTags.push(scriptSrc('https://tpc.googlesyndication.com/pagead/gadgets/html5/api/exitapi.js'));
226
+ } else if (network === 'unity') {
227
+ if (!hasScriptSrc(headTags, 'mraid.js')) headTags.push(scriptSrc('mraid.js'));
228
+ htmlTransforms.push((html) => html.replace(/window\.top\b/g, 'window'));
229
+ } else if (network === 'pangle') {
230
+ headTags.push(scriptSrc('https://sf3-ttcdn-tos.pstatp.com/obj/union-fe-nc/playable/sdk/playable-sdk.js'));
231
+ } else if (network === 'tiktok') {
232
+ headTags.push(scriptSrc('https://sf16-muse-va.ibytedtos.com/obj/union-fe-nc-i18n/playable/sdk/playable-sdk.js'));
233
+ } else if (network === 'mintegral') {
234
+ headTags.push(scriptInline(mintegralSrc));
235
+ } else if (network === 'bigoads') {
236
+ headTags.push(scriptSrc('https://static-web.likeevideo.com/as/common-static/big-data/dsp-public/bgy-mraid-sdk.js'));
237
+ } else if (network === 'liftoff') {
238
+ if (!hasScriptSrc(headTags, 'mraid.js')) headTags.push(scriptSrc('mraid.js'));
239
+ } else if (network === 'snapchat') {
240
+ headTags.push(scriptInline(SNAPCHAT_MOCK));
241
+ } else if (network === 'inmobi') {
242
+ htmlTransforms.push((html) => html.replace(/<\/head>/i, `${buildInmobiScript(buildOptions)}\n</head>`));
243
+ } else if (network === 'social_media') {
244
+ const shareMeta = themePayload && themePayload.mergedData && themePayload.mergedData.gamePlay
245
+ ? themePayload.mergedData.gamePlay.shareMeta
246
+ : null;
247
+ const tags = buildShareMetaHtml(shareMeta);
248
+ if (tags) {
249
+ htmlTransforms.push((html) => html.replace(/<\/head>/i, ` ${tags}\n </head>`));
250
+ }
251
+ }
252
+
253
+ if (network === 'tiktok') {
254
+ extraFiles.push({ from: path.join(RESOURCES, 'tiktok-config.json'), to: 'config.json' });
255
+ } else if (network === 'snapchat') {
256
+ extraFiles.push({ from: path.join(RESOURCES, 'snapchat-config.json'), to: 'config.json' });
257
+ } else if (network === 'bigoads') {
258
+ extraFiles.push({ from: path.join(RESOURCES, 'bigoads-config.json'), to: 'config.json' });
259
+ }
260
+
261
+ return {
262
+ network,
263
+ headTags,
264
+ htmlTransforms,
265
+ extraFiles,
266
+ viewport: network === 'mintegral' ? META.MINTEGRAL_VIEWPORT : META.COMMON_VIEWPORT,
267
+ skipRecommendedMeta: !!buildOptions.skipRecommendedMeta,
268
+ liftoffFont: network === 'liftoff',
269
+ scanStubHtml: buildChannelScanStubHtml(buildOptions),
270
+ };
271
+ }
272
+
273
+ /**
103
274
  * Pack layout for one channel. Zip vs html follows webpack.build.js zipOutputNetworks.
104
275
  * Zip internals: webpack skips HtmlInlineScriptPlugin for mintegral/facebook/adikteev/bigabid.
105
276
  * Cocos already merged to one HTML, so facebook/mintegral stay inlined single index.html;
106
277
  * only adikteev / bigabid keep a separate JS file (CDN).
107
- */function getChannelPackPlan(buildOptions,themePayload){const spec=getChannelInjectionSpec(buildOptions,themePayload);const network=buildOptions.network;const isZip=zipOutputNetworks.includes(network)||!!(buildOptions.zip&&zipOutputAllowedNetworks.includes(network));let layout="inline-html";if(network==="adikteev")layout="adikteev";else if(network==="bigabid")layout="bigabid";return{network,isZip,layout,spec,extraFiles:spec.extraFiles,zipEntries:layout==="adikteev"?["index.html","creative.js","style.css"]:layout==="bigabid"?["index.html","main.js"]:isZip?["index.html",...spec.extraFiles.map(f=>f.to)]:[`${buildOptions.filename||"playable"}.html`]}}module.exports={META,LIFTOFF_FONT,zipOutputNetworks,zipOutputAllowedNetworks,splitJsNetworks,getChannelInjectionSpec,getChannelPackPlan,buildWindowScript,buildChannelScanStubHtml,ADIKTEEV_INDEX,BIGABID_INDEX};
278
+ */
279
+ function getChannelPackPlan(buildOptions, themePayload) {
280
+ const spec = getChannelInjectionSpec(buildOptions, themePayload);
281
+ const network = buildOptions.network;
282
+ const isZip = zipOutputNetworks.includes(network)
283
+ || !!(buildOptions.zip && zipOutputAllowedNetworks.includes(network));
284
+ let layout = 'inline-html';
285
+ if (network === 'adikteev') layout = 'adikteev';
286
+ else if (network === 'bigabid') layout = 'bigabid';
287
+ return {
288
+ network,
289
+ isZip,
290
+ layout,
291
+ spec,
292
+ extraFiles: spec.extraFiles,
293
+ zipEntries: layout === 'adikteev'
294
+ ? ['index.html', 'creative.js', 'style.css']
295
+ : layout === 'bigabid'
296
+ ? ['index.html', 'main.js']
297
+ : isZip
298
+ ? ['index.html', ...spec.extraFiles.map((f) => f.to)]
299
+ : [`${buildOptions.filename || 'playable'}.html`],
300
+ };
301
+ }
302
+
303
+ module.exports = {
304
+ META,
305
+ LIFTOFF_FONT,
306
+ zipOutputNetworks,
307
+ zipOutputAllowedNetworks,
308
+ splitJsNetworks,
309
+ getChannelInjectionSpec,
310
+ getChannelPackPlan,
311
+ buildWindowScript,
312
+ buildChannelScanStubHtml,
313
+ ADIKTEEV_INDEX,
314
+ BIGABID_INDEX,
315
+ };
@@ -1,4 +1,11 @@
1
- "use strict";const path=require("path");const loaderUtils=require("loader-utils");const fs=require("fs");const isObject=value=>typeof value==="object"&&value!==null;const isDataURI=value=>/^data:/i.test(value);/**
1
+ const path = require('path');
2
+ const loaderUtils = require('loader-utils');
3
+ const fs = require('fs');
4
+
5
+ const isObject = (value) => typeof value === 'object' && value !== null;
6
+ const isDataURI = (value) => /^data:/i.test(value);
7
+
8
+ /**
2
9
  * A utility function that allows mapping the value for a given key in an object
3
10
  * [in place](https://en.wikipedia.org/wiki/In-place_algorithm).
4
11
  *
@@ -13,33 +20,72 @@
13
20
  * @param {(value: unknown) => Promise<any>} callback
14
21
  * The callback that either returns the mapped value, or returns a Promise
15
22
  * that resolves to the new value.
16
- */async function mapDeep(obj,key,callback){if(Array.isArray(obj)){for(const item of obj){await mapDeep(item,key,callback)}}else if(isObject(obj)){for(const[k,v]of Object.entries(obj)){if(k===key){obj[k]=await callback(v)}else{await mapDeep(v,key,callback)}}}}/**
23
+ */
24
+ async function mapDeep(obj, key, callback) {
25
+ if (Array.isArray(obj)) {
26
+ for (const item of obj) {
27
+ await mapDeep(item, key, callback);
28
+ }
29
+ } else if (isObject(obj)) {
30
+ for (const [k, v] of Object.entries(obj)) {
31
+ if (k === key) {
32
+ obj[k] = await callback(v);
33
+ } else {
34
+ await mapDeep(v, key, callback);
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ /**
17
41
  * @typedef {import(".").GLTFLoaderDefinition} GLTFLoader
18
42
  * @this {ThisParameterType<GLTFLoader>}
19
43
  * @type {GLTFLoader}
20
- */async function gltfLoader(content){// Destructure loader options and set default values:
21
- const{// inline = true,
22
- // useRelativePaths = true,
23
- // uriResolver = (module) => String(module.default ?? module),
24
- // fileName = '[name].[hash:8].[ext]',
25
- // filePath = '/static/media',
26
- // publicPath = this._compilation?.outputOptions.publicPath ?? '/',
27
- context=this.context}=this.getOptions();// Parse the glTF data:
28
- // console.log(content);
29
- const data=JSON.parse(content);// console.log(data);
30
- // Iterate over the object and map any URIs:
31
- await mapDeep(data,"uri",async uri=>{// Resolve early if the URI cannot be imported as a module:
32
- if(!loaderUtils.isUrlRequest(uri))return uri;// If the URI is a data URI, print a warning and resolve with the original value:
33
- if(isDataURI(uri)){return uri}const requestPath=path.join(context,uri);// console.log(requestPath);
34
- const binContent=fs.readFileSync(requestPath,{encoding:"base64"});return`data:application/octet-stream;base64,${binContent}`});// Stringify the updated data:
35
- const updatedContent=JSON.stringify(data);// Interpolate any file name tokens:
36
- // const interpolatedName = loaderUtils.interpolateName(this, fileName, { content: updatedContent });
37
- // Join the file path and interpolated name:
38
- // const interpolatedPath = path.join(filePath, interpolatedName);
39
- // Emit the file:
40
- // this.emitFile(interpolatedPath, updatedContent, null);
41
- // Join all paths together:
42
- // const fullPath = path.join(publicPath, interpolatedPath);
43
- // Lastly, resolve with either the JSON data or the full output path:
44
- // return `export default ${inline ? updatedContent : `"${fullPath}"`}`;
45
- return`export default ${updatedContent}`}module.exports=gltfLoader;
44
+ */
45
+ async function gltfLoader(content) {
46
+ // Destructure loader options and set default values:
47
+ const {
48
+ // inline = true,
49
+ // useRelativePaths = true,
50
+ // uriResolver = (module) => String(module.default ?? module),
51
+ // fileName = '[name].[hash:8].[ext]',
52
+ // filePath = '/static/media',
53
+ // publicPath = this._compilation?.outputOptions.publicPath ?? '/',
54
+ context = this.context
55
+ } = this.getOptions();
56
+
57
+ // Parse the glTF data:
58
+
59
+ // console.log(content);
60
+ const data = JSON.parse(content);
61
+ // console.log(data);
62
+ // Iterate over the object and map any URIs:
63
+ await mapDeep(data, 'uri', async (uri) => {
64
+ // Resolve early if the URI cannot be imported as a module:
65
+ if (!loaderUtils.isUrlRequest(uri)) return uri;
66
+ // If the URI is a data URI, print a warning and resolve with the original value:
67
+ if (isDataURI(uri)) {
68
+ return uri;
69
+ }
70
+
71
+ const requestPath = path.join(context, uri);
72
+ // console.log(requestPath);
73
+ const binContent = fs.readFileSync(requestPath, { encoding: 'base64' });
74
+ return `data:application/octet-stream;base64,${binContent}`;
75
+ });
76
+ // Stringify the updated data:
77
+ const updatedContent = JSON.stringify(data);
78
+ // Interpolate any file name tokens:
79
+ // const interpolatedName = loaderUtils.interpolateName(this, fileName, { content: updatedContent });
80
+ // Join the file path and interpolated name:
81
+ // const interpolatedPath = path.join(filePath, interpolatedName);
82
+ // Emit the file:
83
+ // this.emitFile(interpolatedPath, updatedContent, null);
84
+ // Join all paths together:
85
+ // const fullPath = path.join(publicPath, interpolatedPath);
86
+ // Lastly, resolve with either the JSON data or the full output path:
87
+ // return `export default ${inline ? updatedContent : `"${fullPath}"`}`;
88
+ return `export default ${updatedContent}`;
89
+ }
90
+
91
+ module.exports = gltfLoader;