← back to Dw Image Shrink
enum-content-refs.js
36 lines
// Extend the referenced-URL universe with NON-product surfaces: blog articles,
// pages, and theme assets (liquid/json/settings). Any cdn.shopify file URL found
// there is "referenced" and must never be deleted. Writes referenced-urls-extra.txt
// (normalized basenames). Uses the CONTENT token (read_content + read_themes).
import fs from 'fs';
import https from 'https';
const SHOP='designer-laboratory-sandbox.myshopify.com';
const TOK=fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env','utf8').split('\n').find(l=>l.startsWith('SHOPIFY_CONTENT_TOKEN=')).split('=').slice(1).join('=').replace(/["' ]/g,'');
const DIR='/Users/macstudio3/Projects/dw-image-shrink';
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
const norm=u=>{if(!u)return'';let s=u.split('?')[0].split('/').pop();s=s.replace(/_\d+x\d+(?=\.[a-z0-9]+$)/i,'');return s.toLowerCase();};
const URLRE=/https?:\/\/cdn\.shopify\.com\/[^\s"'<>)\\]+/gi;
function rest(path){return new Promise((res)=>{const req=https.request({method:'GET',host:SHOP,path,headers:{'X-Shopify-Access-Token':TOK}},r=>{let d='';r.on('data',c=>d+=c);r.on('end',()=>res({status:r.statusCode,link:r.headers.link||'',json:(()=>{try{return JSON.parse(d)}catch(e){return {}}})()}));});req.on('error',()=>res({status:0,json:{}}));req.end();});}
const urls=new Set();
const scan=t=>{const m=(t||'').match(URLRE); if(m) for(const u of m) urls.add(norm(u));};
// pages
let url='/admin/api/2024-10/pages.json?limit=250';
while(url){ const r=await rest(url); for(const p of r.json.pages||[]) scan(p.body_html); const m=r.link.match(/<([^>]+)>;\s*rel="next"/); url=m?m[1].replace('https://'+SHOP,''):null; await sleep(120); }
console.log('after pages:',urls.size);
// blogs + articles
const blogs=(await rest('/admin/api/2024-10/blogs.json?limit=250')).json.blogs||[];
for(const b of blogs){ let au=`/admin/api/2024-10/blogs/${b.id}/articles.json?limit=250`; while(au){ const r=await rest(au); for(const a of r.json.articles||[]){ scan(a.body_html); scan(a.summary_html); if(a.image&&a.image.src)scan(a.image.src);} const m=r.link.match(/<([^>]+)>;\s*rel="next"/); au=m?m[1].replace('https://'+SHOP,''):null; await sleep(120);} }
console.log('after articles:',urls.size);
// collections (custom + smart) descriptions + images
for(const kind of ['custom_collections','smart_collections']){ let cu=`/admin/api/2024-10/${kind}.json?limit=250`; while(cu){ const r=await rest(cu); for(const c of r.json[kind]||[]){ scan(c.body_html); if(c.image&&c.image.src)scan(c.image.src);} const m=r.link.match(/<([^>]+)>;\s*rel="next"/); cu=m?m[1].replace('https://'+SHOP,''):null; await sleep(120);} }
console.log('after collections:',urls.size);
// theme assets (all themes, all assets)
const themes=(await rest('/admin/api/2024-10/themes.json')).json.themes||[];
for(const th of themes){ const al=(await rest(`/admin/api/2024-10/themes/${th.id}/assets.json`)).json.assets||[];
for(const a of al){ if(!/\.(liquid|json|js|css)$/i.test(a.key))continue; const av=(await rest(`/admin/api/2024-10/themes/${th.id}/assets.json?asset[key]=${encodeURIComponent(a.key)}`)).json.asset; if(av&&av.value)scan(av.value); await sleep(60); }
console.log(`theme ${th.id} (${th.role}) scanned, urls now ${urls.size}`);
}
fs.writeFileSync(DIR+'/referenced-urls-extra.txt',[...urls].join('\n'));
console.log('DONE content refs:',urls.size,'→ referenced-urls-extra.txt');