scullyio / scully

The Static Site Generator for Angular apps
https://scully.io/
MIT License
2.56k stars 257 forks source link

transferStateService JSON parse exception due to invalid character in body. #1448

Closed TomJWilliams closed 2 years ago

TomJWilliams commented 3 years ago

🐞 Bug report

First let me state that the team at HeroDevs does an outstanding job and I thoroughly appreciate all of the effort in the creation and development of Scully.

Description

When attempting to retrieve data from the transfer-state service an exception is thrown when attempting to parse the json in the cached state. My reason for bringing this up is the increased adoption with strapi and similar headless cms options. Many use ckeditor or another WYSIWYG editor which will add styling to the object graph as it is added to the content.

I can see that @SanderElias made a commit 9 months, as seen below, ago for the handling of quotes and escaping them for the private saveState method.

SanderGitCommit

🔬 Minimal Reproduction

Set the state with an observable returning json content, in my case from a CMS, where the object graph includes embedded styled components.

💻Your Environment

Angular Version:



Angular CLI: 12.2.2
Node: 14.17.6
Package Manager: npm 6.14.15
OS: win32 x64

Angular: 
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... platform-server, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1202.2 (cli-only)
@angular-devkit/build-angular   
@angular-devkit/core            12.2.2 (cli-only)
@angular-devkit/schematics      12.2.2 (cli-only)
@angular/cli                    12.2.2 (cli-only)
@schematics/angular             12.2.2 (cli-only)
ng-packagr                      
rxjs                            6.6.7 (cli-only)
typescript                      4.4.3 (cli-only)

PS C:\repos\Professional\scully>

Scully Version:



"dependencies": {
    "@angular/animations": "~11.2.14",
    "@angular/cdk": "^11.0.3",
    "@angular/common": "~11.2.14",
    "@angular/compiler": "~11.2.14",
    "@angular/core": "~11.2.14",
    "@angular/forms": "~11.2.14",
    "@angular/platform-browser": "~11.2.14",
    "@angular/platform-browser-dynamic": "~11.2.14",
    "@angular/router": "~11.2.14",
    "@apollo/client": "^3.4.16",
    "@fortawesome/fontawesome-free": "^5.15.1",
    "@scullyio/init": "1.0.1",
    "@scullyio/ng-lib": "^1.0.0",
    "@scullyio/scully": "^1.1.1",
    "@types/chart.js": "^2.9.29",
    "angular-bootstrap-md": "^10.1.1",
    "animate.css": "^4.1.1",
    "apollo-angular": "^2.6.0",
    "chart.js": "^2.9.4",
    "cross-fetch": "^3.1.4",
    "graphql": "^15.6.1",
    "hammerjs": "^2.0.8",
    "ngx-disqus": "^2.4.3",
    "prismjs": "^1.24.1",
    "rxjs": "~6.5.5",
    "tslib": "^2.0.0",
    "zone.js": "~0.10.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^0.1102.14",
    "@angular/cli": "~11.2.14",
    "@angular/compiler-cli": "~11.2.14",
    "@flowaccount/scully-plugin-google-analytics": "0.0.2",
    "@graphql-codegen/cli": "^2.2.1",
    "@graphql-codegen/typescript": "^2.2.4",
    "@graphql-codegen/typescript-apollo-angular": "^3.2.2",
    "@graphql-codegen/typescript-operations": "^2.1.8",
    "@notiz/scully-plugin-rss": "^1.1.0",
    "@types/jasmine": "~3.6.0",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^12.11.1",
    "codelyzer": "^6.0.0",
    "dotenv": "^10.0.0",
    "jasmine-core": "~3.6.0",
    "jasmine-spec-reporter": "~5.0.0",
    "karma": "~6.3.4",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~3.0.2",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "protractor": "~7.0.0",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "~4.1.6",
    "yargs": "^17.1.1"
  }

🔥 Exception or Error

JsonParseError



Uncaught SyntaxError: Unexpected token m in JSON at position 1636

This is the code embedded in the static file which includes the JSON content that is attempting to be parsed from the cache at the time of retrieving the value from the cache with the pluck rxJs method.


function _u(t) {
        t = t.split('/** ___SCULLY_STATE_START___ */')[1].split('/** ___SCULLY_STATE_END___ */')[0];const u = {'_~q~': "'",'_~s~': '/','_~l~': '<','_~g~': '>'};return JSON.parse(t.replace(/_~[^]~/g, (s) => u[s]).replace(/\n/g,'//n'));
      };window['ScullyIO-transfer-state']=_u('/** ___SCULLY_STATE_START___ */{"getArticles-LIVE-true":{"data":{"articles":[{"__typename":"Article","id":"1","slug":"test-article","title":"Test article","description":"_~l~p_~g~This is a test article to validate image support_~l~_~s~p_~g~","featured":false,"author":{"__typename":"Writer","id":"1","name":"David Doe","slug":"david-doe","email":"daviddoe@strapi.io","picture":{"__typename":"UploadFile","id":"24","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~daviddoe_strapi_io_3d3d8df96b.jpg","created_at":"2021-09-12T14:17:18.000Z","updated_at":"2021-09-12T14:17:18.000Z","hash":"daviddoe_strapi_io_3d3d8df96b","mime":"image_~s~jpeg","name":"daviddoe@strapi.io.jpg","provider":"azure-storage","size":587.69},"created_at":"2021-09-12T02:56:18.000Z","updated_at":"2021-09-17T13:49:26.000Z"},"categories":[{"__typename":"Category","id":"1","name":"food","slug":"food","created_at":"2021-09-12T02:56:15.000Z","updated_at":"2021-09-24T22:09:23.000Z"}],"image":{"__typename":"UploadFile","id":"22","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~a_bug_is_becoming_a_meme_on_the_internet_827a5a0d22.jpg","created_at":"2021-09-12T14:17:18.000Z","updated_at":"2021-09-17T12:47:55.000Z","hash":"a_bug_is_becoming_a_meme_on_the_internet_827a5a0d22","mime":"image_~s~jpeg","name":"a-bug-is-becoming-a-meme-on-the-internet.jpg","provider":"azure-storage","size":198.85},"published_at":"2021-09-17T13:46:26.000Z","created_at":"2021-09-17T13:42:26.000Z","updated_at":"2021-10-20T02:14:05.000Z","content":"_~l~p_~g~This is some simple text and we have an HTML snippet however there is no block quote lets see if this causes an issue._~l~_~s~p_~g~_~l~p style=\"margin-left:0px;\"_~g~ _~l~_~s~p_~g~_~l~p style=\"margin-left:0px;\"_~g~HTML:_~l~_~s~p_~g~_~l~div class=\"raw-html-embed\"_~g~_~l~div class=\"article-body-quote-container\"_~g~\n\t_~l~div class=\"row\"_~g~\n  \t_~l~div class=\"col md-4\"_~g~\n    \t_~l~div class=\"card\"_~g~\n  \t\t\t_~l~div class=\"card-body\"_~g~\n        \t_~l~div class=\"d-flex flex-row justify-content-center\"_~g~\n        \t\t_~l~i class=\"fas fa-quote-left pe-2\"_~g~_~l~_~s~i_~g~\n        \t\t_~l~p class=\"px-xl-3\"_~g~\n          \t\tLorem ipsum dolor sit amet,\n          \t\tconsectetur adipisicing elit. Quod eos id officiis hic tenetur\n          \t\tquae quaerat ad velit ab hic tenetur.\n        \t\t_~l~_~s~p_~g~\n        \t\t_~l~i class=\"fas fa-quote-left pe-2\"_~g~_~l~_~s~i_~g~\n        \t_~l~_~s~div_~g~\n      \t_~l~_~s~div_~g~\n      _~l~_~s~div_~g~\n    _~l~_~s~div_~g~\n  _~l~_~s~div_~g~\n_~l~_~s~div_~g~_~l~_~s~div_~g~"},{"__typename":"Article","id":"3","slug":"featured-article","title":"Featured article","description":"_~l~p style=\"text-align:center;\"_~g~This is a featured article about pizza_~l~_~s~p_~g~","featured":true,"author":{"__typename":"Writer","id":"1","name":"David Doe","slug":"david-doe","email":"daviddoe@strapi.io","picture":{"__typename":"UploadFile","id":"24","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~daviddoe_strapi_io_3d3d8df96b.jpg","created_at":"2021-09-12T14:17:18.000Z","updated_at":"2021-09-12T14:17:18.000Z","hash":"daviddoe_strapi_io_3d3d8df96b","mime":"image_~s~jpeg","name":"daviddoe@strapi.io.jpg","provider":"azure-storage","size":587.69},"created_at":"2021-09-12T02:56:18.000Z","updated_at":"2021-09-17T13:49:26.000Z"},"categories":[{"__typename":"Category","id":"3","name":"tech","slug":"tech","created_at":"2021-09-12T02:56:15.000Z","updated_at":"2021-10-04T22:05:53.000Z"}],"image":{"__typename":"UploadFile","id":"30","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~we_love_pizza_301e46a772.jpg","created_at":"2021-09-12T14:17:23.000Z","updated_at":"2021-09-12T14:17:23.000Z","hash":"we_love_pizza_301e46a772","mime":"image_~s~jpeg","name":"we-love-pizza.jpg","provider":"azure-storage","size":955.02},"published_at":"2021-09-19T22:54:40.000Z","created_at":"2021-09-19T22:54:34.000Z","updated_at":"2021-10-04T21:52:33.000Z","content":"_~l~p_~g~Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Egestas tellus rutrum tellus pellentesque eu tincidunt tortor aliquam. Dis parturient montes nascetur ridiculus mus mauris vitae. Amet facilisis magna etiam tempor. Pulvinar neque laoreet suspendisse interdum consectetur libero id. Maecenas accumsan lacus vel facilisis volutpat est. Dictum non consectetur a erat nam. Convallis aenean et tortor at risus viverra adipiscing. Vitae justo eget magna fermentum iaculis. Elementum pulvinar etiam non quam lacus suspendisse faucibus interdum posuere. Ac feugiat sed lectus vestibulum mattis. Ac turpis egestas sed tempus urna. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Ipsum faucibus vitae aliquet nec. Eget arcu dictum varius duis._~l~_~s~p_~g~_~l~p_~g~Eget nulla facilisi etiam dignissim diam quis enim. Rhoncus est pellentesque elit ullamcorper dignissim. Quisque id diam vel quam elementum pulvinar etiam non. Morbi non arcu risus quis varius quam quisque id. Sem viverra aliquet eget sit amet tellus cras adipiscing enim. Ac orci phasellus egestas tellus rutrum tellus pellentesque eu. Interdum posuere lorem ipsum dolor sit amet. Cursus in hac habitasse platea dictumst. Elit duis tristique sollicitudin nibh sit amet commodo nulla facilisi. Dolor sit amet consectetur adipiscing elit duis tristique sollicitudin nibh. Morbi non arcu risus quis varius quam. Ridiculus mus mauris vitae ultricies leo integer malesuada nunc._~l~_~s~p_~g~_~l~p_~g~Cursus sit amet dictum sit amet justo donec. Congue mauris rhoncus aenean vel elit scelerisque mauris pellentesque. Mollis aliquam ut porttitor leo a diam sollicitudin tempor. Cursus sit amet dictum sit amet justo donec enim. Malesuada fames ac turpis egestas integer eget aliquet nibh. Ullamcorper dignissim cras tincidunt lobortis feugiat vivamus. Massa tincidunt nunc pulvinar sapien et ligula ullamcorper malesuada proin. Odio tempor orci dapibus ultrices in. Dictum at tempor commodo ullamcorper a lacus vestibulum. Feugiat nisl pretium fusce id velit ut. Venenatis a condimentum vitae sapien pellentesque habitant. A condimentum vitae sapien pellentesque. Volutpat diam ut venenatis tellus in metus vulputate eu. Duis at consectetur lorem donec. Ut tortor pretium viverra suspendisse potenti nullam ac tortor. Ipsum dolor sit amet consectetur. Pretium quam vulputate dignissim suspendisse. Nibh ipsum consequat nisl vel pretium lectus quam id leo. Lectus quam id leo in vitae._~l~_~s~p_~g~_~l~p_~g~A arcu cursus vitae congue mauris rhoncus aenean vel elit. Volutpat lacus laoreet non curabitur. Nibh mauris cursus mattis molestie a. Ultrices sagittis orci a scelerisque. Convallis tellus id interdum velit laoreet id donec ultrices tincidunt. Venenatis a condimentum vitae sapien pellentesque habitant morbi tristique senectus. Diam ut venenatis tellus in metus vulputate. Turpis massa tincidunt dui ut ornare lectus sit amet. Consequat semper viverra nam libero justo laoreet sit amet cursus. Eget nullam non nisi est sit. Nibh cras pulvinar mattis nunc sed. Scelerisque purus semper eget duis at tellus at urna. Gravida rutrum quisque non tellus orci ac auctor augue mauris. Morbi quis commodo odio aenean sed adipiscing diam._~l~_~s~p_~g~_~l~p_~g~Pulvinar sapien et ligula ullamcorper malesuada. Porttitor eget dolor morbi non arcu risus quis varius. Non curabitur gravida arcu ac tortor dignissim convallis aenean. Feugiat pretium nibh ipsum consequat nisl vel pretium. Amet cursus sit amet dictum sit amet justo donec enim. Massa ultricies mi quis hendrerit. Eleifend mi in nulla posuere sollicitudin aliquam ultrices sagittis orci. Amet luctus venenatis lectus magna. Nam aliquam sem et tortor consequat id porta nibh venenatis. Lacus viverra vitae congue eu consequat ac felis donec. Tincidunt nunc pulvinar sapien et ligula ullamcorper. Auctor urna nunc id cursus metus aliquam eleifend mi in. Id semper risus in hendrerit gravida rutrum quisque non tellus. Gravida cum sociis natoque penatibus et magnis dis. Venenatis urna cursus eget nunc scelerisque viverra mauris. Volutpat consequat mauris nunc congue._~l~_~s~p_~g~"},{"__typename":"Article","id":"4","slug":"test-writer","title":"Test writer","description":"_~l~p_~g~This is a test of the article by writer slug_~l~_~s~p_~g~","featured":false,"author":{"__typename":"Writer","id":"5","name":"Tom Williams","slug":"tom-williams","email":"tom@theenterpriseprogrammer.com","picture":{"__typename":"UploadFile","id":"31","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~Tom_Williams_df55191114.jpeg","created_at":"2021-10-17T11:47:36.000Z","updated_at":"2021-10-17T11:47:36.000Z","hash":"Tom_Williams_df55191114","mime":"image_~s~jpeg","name":"TomWilliams.jpeg","provider":"azure-storage","size":3.07},"created_at":"2021-09-20T01:04:42.000Z","updated_at":"2021-10-17T11:47:57.000Z"},"categories":[{"__typename":"Category","id":"2","name":"story","slug":"story","created_at":"2021-09-12T02:56:15.000Z","updated_at":"2021-09-19T22:46:07.000Z"}],"image":{"__typename":"UploadFile","id":"25","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~beautiful_picture_2a7b29591a.jpg","created_at":"2021-09-12T14:17:18.000Z","updated_at":"2021-09-12T14:17:19.000Z","hash":"beautiful_picture_2a7b29591a","mime":"image_~s~jpeg","name":"beautiful-picture.jpg","provider":"azure-storage","size":585.12},"published_at":"2021-09-20T01:11:08.000Z","created_at":"2021-09-20T01:11:03.000Z","updated_at":"2021-10-17T11:47:56.000Z","content":"_~l~p_~g~This is a test of the writer search by slug_~l~_~s~p_~g~"},{"__typename":"Article","id":"6","slug":"code-snippet-test","title":"Code Snippet Test","description":"_~l~p_~g~This is a multiline description for an article that implements a code snippet section. We will see how the code snippet highlighting performs and how the ellipsis for the multiline description performs as well._~l~_~s~p_~g~","featured":false,"author":{"__typename":"Writer","id":"5","name":"Tom Williams","slug":"tom-williams","email":"tom@theenterpriseprogrammer.com","picture":{"__typename":"UploadFile","id":"31","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~Tom_Williams_df55191114.jpeg","created_at":"2021-10-17T11:47:36.000Z","updated_at":"2021-10-17T11:47:36.000Z","hash":"Tom_Williams_df55191114","mime":"image_~s~jpeg","name":"TomWilliams.jpeg","provider":"azure-storage","size":3.07},"created_at":"2021-09-20T01:04:42.000Z","updated_at":"2021-10-17T11:47:57.000Z"},"categories":[{"__typename":"Category","id":"3","name":"tech","slug":"tech","created_at":"2021-09-12T02:56:15.000Z","updated_at":"2021-10-04T22:05:53.000Z"}],"image":{"__typename":"UploadFile","id":"27","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~the_internet_s_own_boy_42a62a6552.jpg","created_at":"2021-09-12T14:17:20.000Z","updated_at":"2021-09-12T14:17:21.000Z","hash":"the_internet_s_own_boy_42a62a6552","mime":"image_~s~jpeg","name":"the-internet-s-own-boy.jpg","provider":"azure-storage","size":91.55},"published_at":"2021-10-17T11:46:04.000Z","created_at":"2021-10-17T11:46:00.000Z","updated_at":"2021-10-17T11:49:28.000Z","content":"_~l~p_~g~And here, we, go!!_~l~_~s~p_~g~_~l~p_~g~ _~l~_~s~p_~g~_~l~p_~g~This is a code example._~l~_~s~p_~g~_~l~pre_~g~_~l~code class=\"language-typescript\"_~g~export function playground(_options: any): Rule {\n  return (tree: Tree, _context: SchematicContext) => {\n    console.log(\"schematic works\");\n    return tree;\n  };_~l~_~s~code_~g~_~l~_~s~pre_~g~_~l~p_~g~}_~l~_~s~p_~g~_~l~p_~g~ _~l~_~s~p_~g~_~l~p_~g~This is another code example._~l~_~s~p_~g~_~l~div class=\"raw-html-embed\"_~g~_~l~pre_~g~  _~l~code [highlight]=\"code\"_~g~export function playground(_options: any): Rule {\n  return (tree: Tree, _context: SchematicContext) => {\n    console.log(\"schematic works\");\n    return tree;\n  };_~l~_~s~code_~g~\n_~l~_~s~pre_~g~_~l~_~s~div_~g~"}]},"loading":false,"networkStatus":7}}/** ___SCULLY_STATE_END___ */')}
TomJWilliams commented 3 years ago

The specific issue is if the content persisted in the cache contains dome elements with inline styles it will fail the JSON.parse with an unexpected token.

In this case the rich text editor added an inline style of style=\"margin-left:0px;\" which generates the error invalid token m at position xxx. In this instance the character is m however, I believe this would be any character with an inline style generated by a rich text editor on a CMS. I need to get into work I will take another look at this later this evening.

TomJWilliams commented 3 years ago

After reviewing this more this issue is directly related to the way the content is parsed during persistence. Specifically the escaping of quotations, \n, or \t characters. I have included a snippet below which illustrates the example and highly levies code from the transfer-state.service.ts class.

    // Function to parse scully state this is based off the pluc functionality and is the same less syntactical sugar
function _u(key, t) {
    // Create the state tree
    const stateTree = t.split('/** ___SCULLY_STATE_START___ */')[1].split('/** ___SCULLY_STATE_END___ */')[0];

  // Retrieve the record from the state tree
  let record = stateTree.split(`"${key}":`)[1];

  if (!record) {
    return;
  }

  let parsedRecord = record.replace(/_~d~/g,'\\\\"').replace(/_~[^]~/g, (s) => interpolationCharacters[s]).replace(/\\n/g,'\\\\n').replace(/\\t/g,'\\\\t').replace(/\\r/g,'\\\\r');
  console.log(parsedRecord);
  let jsonRecord = JSON.parse(parsedRecord);
  //console.log(parsedRecord);
}

// Moq uncorrected scully state
let uncorrectedScullyState = '/** ___SCULLY_STATE_START___ */{"getArticles-LIVE-true":{"data":{"articles":[{"__typename":"Article","id":"1","slug":"test-article","title":"Test article","description":"_~l~p_~g~This is a test article to validate image support_~l~_~s~p_~g~","featured":false,"author":{"__typename":"Writer","id":"1","name":"David Doe","slug":"david-doe","email":"daviddoe@strapi.io","picture":{"__typename":"UploadFile","id":"24","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~daviddoe_strapi_io_3d3d8df96b.jpg","created_at":"2021-09-12T14:17:18.000Z","updated_at":"2021-09-12T14:17:18.000Z","hash":"daviddoe_strapi_io_3d3d8df96b","mime":"image_~s~jpeg","name":"daviddoe@strapi.io.jpg","provider":"azure-storage","size":587.69},"created_at":"2021-09-12T02:56:18.000Z","updated_at":"2021-09-17T13:49:26.000Z"},"categories":[{"__typename":"Category","id":"1","name":"food","slug":"food","created_at":"2021-09-12T02:56:15.000Z","updated_at":"2021-09-24T22:09:23.000Z"}],"image":{"__typename":"UploadFile","id":"22","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~a_bug_is_becoming_a_meme_on_the_internet_827a5a0d22.jpg","created_at":"2021-09-12T14:17:18.000Z","updated_at":"2021-09-17T12:47:55.000Z","hash":"a_bug_is_becoming_a_meme_on_the_internet_827a5a0d22","mime":"image_~s~jpeg","name":"a-bug-is-becoming-a-meme-on-the-internet.jpg","provider":"azure-storage","size":198.85},"published_at":"2021-09-17T13:46:26.000Z","created_at":"2021-09-17T13:42:26.000Z","updated_at":"2021-10-20T02:14:05.000Z","content":"_~l~p_~g~This is some simple text and we have an HTML snippet however there is no block quote lets see if this causes an issue._~l~_~s~p_~g~_~l~p style=\"margin-left:0px;\"_~g~&nbsp;_~l~_~s~p_~g~_~l~p style=\"margin-left:0px;\"_~g~HTML:_~l~_~s~p_~g~_~l~div class=\"raw-html-embed\"_~g~_~l~div class=\"article-body-quote-container\"_~g~\n\t_~l~div class=\"row\"_~g~\n  \t_~l~div class=\"col md-4\"_~g~\n    \t_~l~div class=\"card\"_~g~\n  \t\t\t_~l~div class=\"card-body\"_~g~\n        \t_~l~div class=\"d-flex flex-row justify-content-center\"_~g~\n        \t\t_~l~i class=\"fas fa-quote-left pe-2\"_~g~_~l~_~s~i_~g~\n        \t\t_~l~p class=\"px-xl-3\"_~g~\n          \t\tLorem ipsum dolor sit amet,\n          \t\tconsectetur adipisicing elit. Quod eos id officiis hic tenetur\n          \t\tquae quaerat ad velit ab hic tenetur.\n        \t\t_~l~_~s~p_~g~\n        \t\t_~l~i class=\"fas fa-quote-left pe-2\"_~g~_~l~_~s~i_~g~\n        \t_~l~_~s~div_~g~\n      \t_~l~_~s~div_~g~\n      _~l~_~s~div_~g~\n    _~l~_~s~div_~g~\n  _~l~_~s~div_~g~\n_~l~_~s~div_~g~_~l~_~s~div_~g~"}]}}/** ___SCULLY_STATE_END___ */'

// Moq corrected scully state
let correctedScullyState = '/** ___SCULLY_STATE_START___ */{"getArticles-LIVE-true":{"data":{"articles":[{"__typename":"Article","id":"1","slug":"test-article","title":"Test article","description":"_~l~p_~g~This is a test article to validate image support_~l~_~s~p_~g~","featured":false,"author":{"__typename":"Writer","id":"1","name":"David Doe","slug":"david-doe","email":"daviddoe@strapi.io","picture":{"__typename":"UploadFile","id":"24","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~daviddoe_strapi_io_3d3d8df96b.jpg","created_at":"2021-09-12T14:17:18.000Z","updated_at":"2021-09-12T14:17:18.000Z","hash":"daviddoe_strapi_io_3d3d8df96b","mime":"image_~s~jpeg","name":"daviddoe@strapi.io.jpg","provider":"azure-storage","size":587.69},"created_at":"2021-09-12T02:56:18.000Z","updated_at":"2021-09-17T13:49:26.000Z"},"categories":[{"__typename":"Category","id":"1","name":"food","slug":"food","created_at":"2021-09-12T02:56:15.000Z","updated_at":"2021-09-24T22:09:23.000Z"}],"image":{"__typename":"UploadFile","id":"22","url":"https:_~s~_~s~the-enterprise-programmer-cms-assets.azureedge.net_~s~cms_~s~assets_~s~a_bug_is_becoming_a_meme_on_the_internet_827a5a0d22.jpg","created_at":"2021-09-12T14:17:18.000Z","updated_at":"2021-09-17T12:47:55.000Z","hash":"a_bug_is_becoming_a_meme_on_the_internet_827a5a0d22","mime":"image_~s~jpeg","name":"a-bug-is-becoming-a-meme-on-the-internet.jpg","provider":"azure-storage","size":198.85},"published_at":"2021-09-17T13:46:26.000Z","created_at":"2021-09-17T13:42:26.000Z","updated_at":"2021-10-20T02:14:05.000Z","content":"_~l~p_~g~This is some simple text and we have an HTML snippet however there is no block quote lets see if this causes an issue._~l~_~s~p_~g~_~l~p style=\\"margin-left:0px;\\"_~g~&nbsp;_~l~_~s~p_~g~_~l~p style=\\"margin-left:0px;\\"_~g~HTML:_~l~_~s~p_~g~_~l~div class=\\"raw-html-embed\\"_~g~_~l~div class=\\"article-body-quote-container\\"_~g~\\n\\t_~l~div class=\\"row\\"_~g~\\n  \\t_~l~div class=\\"col md-4\\"_~g~\\n    \\t_~l~div class=\\"card\\"_~g~\\n  \\t\\t\\t_~l~div class=\\"card-body\\"_~g~\\n        \\t_~l~div class=\\"d-flex flex-row justify-content-center\\"_~g~\\n        \\t\\t_~l~i class=\\"fas fa-quote-left pe-2\\"_~g~_~l~_~s~i_~g~\\n        \\t\\t_~l~p class=\\"px-xl-3\\"_~g~\\n          \\t\\tLorem ipsum dolor sit amet,\\n          \\t\\tconsectetur adipisicing elit. Quod eos id officiis hic tenetur\\n          \\t\\tquae quaerat ad velit ab hic tenetur.\\n        \\t\\t_~l~_~s~p_~g~\\n        \\t\\t_~l~i class=\\"fas fa-quote-left pe-2\\"_~g~_~l~_~s~i_~g~\\n        \\t_~l~_~s~div_~g~\\n      \\t_~l~_~s~div_~g~\\n      _~l~_~s~div_~g~\\n    _~l~_~s~div_~g~\\n  _~l~_~s~div_~g~\\n_~l~_~s~div_~g~_~l~_~s~div_~g~"}]}}/** ___SCULLY_STATE_END___ */'

// Invoke function
_u('getArticles-LIVE-true',uncorrectedScullyState);
SanderElias commented 3 years ago

@TomJWilliams Thanks for digging into this. Solving it is a bit hard, as the code is fully generated from the service. There is meta-escaping involved. Also, embedding HTML content in the transfer-state might not be the ideal solution. You can use scully-content for that. You can do that on a per route base, and pull the content during build using a function plugin

'/routeByCMS/:id' : {
  type: default, // or whatever 
  postRenderers: ['contentText'], // if you have defaultRenderes, pull them in!
  contentType: 'html', (or HTML or plain text, or whatever)
  content: async (route:HandledRoute) => {/** extract Id from route, fetch content from CMS and returnit as sting **/ {
}

However, I would still love to get a PR that solves this issue. As I'm currently lacking the time to prioritize this issue.

TomJWilliams commented 3 years ago

@SanderElias My apologies for the delay in response. I do not disagree with you. I struggled over the decision to use the transfer-state service. Ultimately what led to the decision is that this content is dynamic, fed from a user query via the CMS. Thus I am using transfer-state to cache the initial response from the service decreasing the page load time. Then if the user changes the query, the key for the cache does not match and the service is called instead. I believe the core of the issue is the meta-escaping and the fact that the HTML with class information is returned via Strapi. Let me look into this more, I am also working on potentially changing out the backend to Sanity. This changes the structure of what the cache would be and I can refactor my Angular app to use a component factory. This should also resolve the fundamental issue given that Sanity's data structure is fundamentally different than html.

SanderElias commented 3 years ago

@TomJWilliams I'm beginning to think we need a separate way of storing arbitrary data, outside of the page. Just like we do with the data.json which carries a copy of the date in the transferState. Or, perhaps we need something that works similar to scully-content but saves the data into templates outside of the angular app. It is whole well possible to build plugins that already do those things.

SanderElias commented 2 years ago

I have been looking into this one more, and it pretty much is a duplicate of #1567. So, I'm closing this issue, to track progress there. (also, the PR targeting that bug will for 95% close this one too)