function prepareScript(script, env) {
  const { custom, meta, props } = script;
  const { id } = props;
  const { [S_REQUIRE]: require, [RUN_AT]: runAt } = env;
  const code = env[S_CODE][id];
  const dataKey = getUniqId();
  const winKey = getUniqId();
  const plantKey = { data: dataKey, win: winKey };
  const displayName = getScriptName(script);
  const pathMap = custom.pathMap || {};
  const wrap = !meta[UNWRAP];
  const wrapTryCatch = wrap && IS_FIREFOX; // FF doesn't show errors in content script's console
  const { grant, [TL_AWAIT]: topLevelAwait } = meta;
  const startIIFE = topLevelAwait ? 'await(async' : '(';
  const numGrants = grant.length;
  const grantNone = !numGrants || numGrants === 1 && grant[0] === 'none';
  // Storing slices separately to reuse JS-internalized strings for code in our storage cache
  const injectedCode = [];
  const metaCopy = meta::mapEntry(null, pluralizeMeta);
  const metaStrMatch = METABLOCK_RE.exec(code);
  let codeIndex;
  let tmp;
  for (const key of META_KEYS_TO_ENSURE) {
    if (metaCopy[key] == null) metaCopy[key] = '';
  }
  for (const [key, from] of META_KEYS_TO_ENSURE_FROM) {
    if (!metaCopy[key] && (tmp = metaCopy[from])) {
      metaCopy[key] = tmp;
    }
  }
  if (wrap) {
    // TODO: push winKey/dataKey as separate chunks so we can change them for each injection?
    injectedCode.push('window.', winKey, '=',
      wrapTryCatch && topLevelAwait ? 'async ' : '',
      'function ', dataKey, '(',
      // using a shadowed name to avoid scope pollution
      grantNone ? GRANT_NONE_VARS : 'GM',
      wrapTryCatch ? `,${dataKey}){try{` : '){',
      grantNone ? '' : 'with(this)with(c)delete c,',
      !topLevelAwait ? '(' : wrapTryCatch ? startIIFE : '(async',
      // hiding module interface from @require'd scripts so they don't mistakenly use it
      '(define,module,exports)=>{');
  }
  tmp = false;
  for (const url of meta[S_REQUIRE]) {
    const req = require[pathMap[url] || url];
    if (/\S/.test(req)) {
      injectedCode.push(...[
        tmp && isUnsafeConcat(req) && ';',
        req,
        !NEWLINE_END_RE.test(req) && '\n',
      ].filter(Boolean));
      tmp = true;
    }
  }
  if (tmp && isUnsafeConcat(code)) {
    injectedCode.push(';');
  }
  codeIndex = injectedCode.length;
  injectedCode.push(code);
  // adding a new line in case the code ends with a line comment
  injectedCode.push(...[
    !NEWLINE_END_RE.test(code) && '\n',
    wrapTryCatch ? `})()}catch(e){${dataKey}(e)}}` : wrap && `})()}`,
    // 0 at the end to suppress errors about non-cloneable result of executeScript in FF
    IS_FIREFOX && ';0',
    '\n//# sourceURL=', getScriptPrettyUrl(script, displayName),
  ].filter(Boolean));
  return {
    code: '',
    displayName,
    gmi: {
      scriptWillUpdate: !!script.config.shouldUpdate,
      uuid: props.uuid,
    },
    id,
    key: plantKey,
    meta: metaCopy,
    pathMap,
    [__CODE]: injectedCode,
    [INJECT_INTO]: normalizeScriptRealm(custom, meta),
    [META_STR]: [
      '',
      codeIndex,
      tmp = metaStrMatch && (metaStrMatch.index + metaStrMatch[1].length),
      tmp + metaStrMatch?.[4].length,
    ],
    [RUN_AT]: runAt[id],
  };
}
@param {VMScript} script
@param {VMInjection.EnvStart} env
@return {VMInjection.Script}